There is now code for creating strips like the find and replace
strips from Lua code. An example looks like
http://scintilla.org/UserStrip.png
The first method is StripShow which takes a string description of
the user interface then builds and displays it as a strip at the
bottom of the application window. There are 4 supported elements:
static text, editable text, combo boxes and buttons. These are
surrounded by different indicator characters: ' for static text; []
for editable text; {} for combo boxes; and () for buttons. There can
also be a newline to start a new line and a ! to show a close box on
Windows only. Buttons may include accelerator keys prefixed with &.
For example, the code
scite.StripShow("!'Explanation:'{}(&Search)\n'Name:'[Name](OK)(Cancel)")
shows the strip displayed in the picture. The strip can be closed
by passing an empty string.
On GTK+ a table is used for layout and since that has worked quite
well for strips and dialogs, the approach was copied to the other
platforms. It is not yet as capable on the other platforms yet as on
GTK+ - columns containing editable text and combo boxes can expand and
other columns are fixed to the their natural width of their widest
element.
Events from the user are communicated back to the script through
the OnUser function which takes an element number (starting at 0 and
including static text elements) and a change type (clicked=1,
change=2, focusIn=3, focusOut=4). 'clicked' is for button presses,
'change' for changes to editable text or the editable text part of a
combo boxes and 'focusIn' and 'focusOut' are when the user moves focus
between elements. The value of editable text or combo boxes can be
retrieved with StripValue(element).
There are some bugs and limitations with these events currently.
Focus events may not occur or may occur only when text is edited.
Selecting an item from the list in a combo box may not send a 'change'
event.
The editable part of combo boxes and editable text can be set with
StripSet(element, value) and the list part of combo boxes can be set
with StripSetList(element, value) where the items in 'value' are
separated with new lines.
Available from Hg and from
http://www.scintilla.org/scite.zip Source
http://www.scintilla.org/wscite.zip Windows executable
Neil
> the OnUser function
The event receiving function is actually called OnStrip.
Neil
Cool feature! Thanks.
I have a couple of Lua scripts using the Parameters dialog as input but it is quite
impractical, I will try and use this feature, and probably hesitate less to parametrize my
scripts.
--
Philippe Lhoste
-- (near) Paris -- France
-- http://Phi.Lho.free.fr
-- -- -- -- -- -- -- -- -- -- -- -- -- --
> There is now code for creating strips like the find and replace
> strips from Lua code. An example looks like
Nice feature!
Unfortunately, there are a couple of bugs left (all of the following is
for GTK+).
> [...] Buttons may include accelerator keys prefixed with&.
On GTK+, the letters currently need to be prefixed with _, which is the
GTK-way of indicating accelerator keys. The documentation text either
needs to be updated to reflect this, but preferably & should work on
GTK+ too, like it does for the menues. This would allow writing
cross-platform Lua scripts.
It is worth noting that _ also has an effet on labels ("static text"):
the letter is shown underlined, indicating an accelerator key. However,
the label is not associated with anything and activating the key does
nothing. Possible solutions: disabling accelerator keys for labels
altogether, or associating the label with the field next to it if there
is one (say editable text or combo box), so that activating the
accelerator key gives focus to the field.
> Events from the user are communicated back to the script through
> the OnUser function [...]
As you corrected in another email, the function is called OnStrip. The
documentation should be corrected accordingly.
> The editable part of combo boxes and editable text can be set with
> StripSet(element, value) and the list part of combo boxes can be set
> with StripSetList(element, value) where the items in 'value' are
> separated with new lines.
On GTK+, StripSet() doesn't work for combo boxes. The incriminating line
is the following one in the UserStrip::Set function in gtk/SciTEGTK.cxx:
if (ctl->controlType == UserControl::ucEdit) {
However, I've not been able to change it to make it work (my C++ skills
are very limited) � just adding "ctl->controlType ==
UserControl::ucCombo" won't work, as there is some casting involved.
Last bug noticed: if you're on the last element in a strip and you press
Tab, the focus seems to go nowhere.
Regards,
Nicolas
> On GTK+, the letters currently need to be prefixed with _, which is the
> GTK-way of indicating accelerator keys.
Changed to use '&' so it works cross-platform.
> It is worth noting that _ also has an effet on labels ("static text"): the
> letter is shown underlined, indicating an accelerator key. However, the
> label is not associated with anything and activating the key does nothing.
'&' now displayed as a temporary underline for static text but does
not yet move focus.
> On GTK+, StripSet() doesn't work for combo boxes.
Fixed.
> Last bug noticed: if you're on the last element in a strip and you press
> Tab, the focus seems to go nowhere.
This is more trouble so may not be fixed for 3.0.4.
> Is this feature limited to one function? I guess not, but I can't understand
> how it works.
Yes, SciTE calls functions with defined names for events. There is
no facility to define particular functions to be active in some
context. There is a script that adds something like this
http://lua-users.org/wiki/SciteExtMan
Neil
> It is worth noting that _ also has an effet on labels ("static text"): the
> letter is shown underlined, indicating an accelerator key. However, the
> label is not associated with anything and activating the key does nothing.
> Possible solutions: disabling accelerator keys for labels altogether, or
> associating the label with the field next to it if there is one (say
> editable text or combo box), so that activating the accelerator key gives
> focus to the field.
Implemented by moving focus to next widget that accepts focus.
> Last bug noticed: if you're on the last element in a strip and you press
> Tab, the focus seems to go nowhere.
Tab should now work on last element.
Fixes available from Hg.
Neil
Nicolas Chachereau:> It is worth noting that _ also has an effet on labels ("static text"): the
> letter is shown underlined, indicating an accelerator key. However, the
> label is not associated with anything and activating the key does nothing.
> Possible solutions: disabling accelerator keys for labels altogether, or
> associating the label with the field next to it if there is one (say
> editable text or combo box), so that activating the accelerator key gives
> focus to the field.Implemented by moving focus to next widget that accepts focus.
Also if first widget (excluding labal) is not input box, pressing Enter does not do anything. OTOH if first widget is input box, pressing Enter activates next widget (button)
Using the sample script, when I hit Enter with focus on any component, I get the 'clicked'
event on the first component (number 0: it would have been more Lua-ish to number it 1...).
Also (but Neil warned about this), the focus event is unreliable. In this demo script, it
happens only on the text field.
The scite.StripSet(1, "...") on a combo box doesn't seem to work.
Ah, while modifying the script, I found a strange bug: I can type in the main SciTE
buffer, but when I hit Return, the strip eats the event! I get the event displayed in the
output pane, and no newline in edition buffer...
Good point, I can hit Escape to close the dialogue (then I get back regular editing).
Tests done with SciTE 3.0.4 on Windows XP.
Note: the image format of the script isn't very practical, I give here my version to play
with:
function showStripAndPlay()
if editor.SelectionStart == editor.SelectionEnd then
scite.StripShow("")
else
scite.StripShow("!'Explanation:'{}(&Search)\n'Name:'[Name](OK)(Cancel)")
scite.StripSetList(1, "Apple\nBanana\nLemon\nOrange\nPear\nKiwi")
scite.StripSet(1, "<choose a fruit>")
scite.StripSet(4, "A longer name")
end
end
function OnStrip(control, change)
local changeNames = { 'unknown', 'clicked', 'change', 'focusIn', 'focusOut' }
print("OnStrip: " .. control .. " -> " .. changeNames[change + 1])
if control == 2 and change == 1 then -- Click in the combo box
local search = scite.StripValue(1)
print("Searching for " .. search)
elseif control == 5 and change == 1 then -- Click on OK
print("Control states: list=" .. scite.StripValue(1) .. ", text=" .. scite.StripValue(4))
scite.StripShow("") -- Hide the dialog
elseif control == 6 and change == 1 then -- Click on Cancel
scite.StripShow("") -- Just hide the dialog
end
end
Have you ext.lua.reset=1 in your settings? Useful for editing and debugging scripts, so I
have it always set. I never use persistence between buffers. Perhaps setting a SciTE
property can work for this.
But then focus is lost somewhere outside of the buffers (typing after hitting Escape does
nothing, until I click in the edit frame).
Also a funny bug: if I call StripShow (adults only?) twice with non-empty parameters (I
assign a shortcut key to the function showing it), the second time, the strip content is
pushed on the left, the controls are stacked on the first third of the strip... Not a show
stopper, but I thought I should report it.
> Would it be possible to define default focus widget?
> Maybe scite.StripSetFocus(number)?
> Right now first widget excluding label is focused
Since forms are normally layed out to be filled in visual order, is
there a need for this?
There are many features that could be added, from password entry
fields to changing colours, and I don't want to be continually adding
code for things that aren't really needed.
Neil
The scite.StripSet(1, "...") on a combo box doesn't seem to work.
Note: the image format of the script isn't very practical, I give here my version to playwith:
zetah:
Since forms are normally layed out to be filled in visual order, is
there a need for this?
I played with the concept. I like the compact textual format, but the usage of special
symbols and numbers (with off-by-one indexes) isn't very practical.
As an exercise, I made a new format for the GUI: it is parsed into the compact format.
It allows to name components (last field).
And the combo boxes are automatically filled.
Note: I accidentally called scite.StripShow("testGUI") and it crashes (closed by Windows!)
SciTE...
-- Globals
exampleGUI =
{
{ 'close' },
{ 'label', "Explanation:" },
{ 'combo', "Apple\nBanana\nLemon\nOrange\nPear\nKiwi", 'fruitNames' },
{ 'button', "&Search", 'search' },
{ 'eol' },
{ 'label', "Name:" },
{ 'text', "Name", 'name' },
{ 'button', "OK", 'ok' },
{ 'button', "Cancel", 'cancel' },
}
testGUI =
{
{ 'label', "Numbers:" },
{ 'combo', "ichi\nni\nsan\nshi\ngo\nroku\nshichi\nnana\nhachi\nkyu\njû", 'numbers' },
{ 'label', "Days:" },
{ 'combo', "getsu\nka\nsui\nmoku\nkin\ndo\nnichi", 'days' },
{ 'label', "Seasons:" },
{ 'combo', "fuyu 春\nharu 夏\nnatsu 秋\naki 冬", 'seasons' },
{ 'eol' },
{ 'button', "OK", 'ok' },
{ 'button', "Cancel", 'cancel' },
{ 'button', "Help" }, -- Test no name...
{ 'close' },
}
function buildGUI(guiDesc)
local gui = {}
local combos = {}
local names = {}
local componentCount = 0
for n, component in ipairs(guiDesc) do
local ctype = component[1]
local added = nil
if ctype == 'close' then
added= '!'
elseif ctype == 'eol' then
added= '\n'
elseif ctype == 'label' then
componentCount = componentCount + 1
added= "'" .. component[2] .. "'"
elseif ctype == 'button' then
componentCount = componentCount + 1
added= "(" .. component[2] .. ")"
elseif ctype == 'combo' then
componentCount = componentCount + 1
added= '{}'
if #component > 1 then
combos[componentCount] = component[2]
end
elseif ctype == 'text' then
componentCount = componentCount + 1
local text = component[2] or ''
added= "[" .. text .. "]"
end
if added ~= nil then
gui[#gui + 1] = added
if #component > 2 then
names[componentCount] = component[3]
end
end
end
local guiString = table.concat(gui)
return guiString, combos, names
end
-- Poor attempt at context management...
function showStrip(guiName)
local gui = _G[guiName]
local guiString, combos, names = buildGUI(gui)
scite.StripShow(guiString)
for n, list in pairs(combos) do
scite.StripSetList(n - 1, list)
end
props['currentStrip'] = guiName
end
function getCurrentSettings()
-- Not economical, but slowness is probably unnoticeable!
-- We cannot keep global variables between calls, because of ext.lua.reset=1
-- And props cannot store a table
local gui = _G[props['currentStrip']]
local _, _, names = buildGUI(gui)
local controls = {}
for n, name in pairs(names) do
controls[name] = n - 1
end
return gui, names, controls
end
function showStripAndPlay()
if editor.SelectionStart == editor.SelectionEnd then
--~ scite.StripShow("'Numbers:'{}'Days:'{}'Seasons:'{}\n(OK)(Cancel)(Help)!")
showStrip("testGUI")
else
showStrip('exampleGUI')
--~ scite.StripShow("'Explanation:'{}(&Search)\n'Name:'[Name](OK)!(Cancel)")
--~ scite.StripSetList(1, "Apple\nBanana\nLemon\nOrange\nPear\nKiwi")
--~ scite.StripSet(1, "<choose a fruit>")
scite.StripSet(4, "A longer name")
end
end
function OnStrip(control, change)
local eventClick, eventChange, eventFocusIn, eventFocusOut = 1, 2, 3, 4
local changeNames = { 'unknown', 'clicked', 'change', 'focusIn', 'focusOut' }
local currentStrip, names, controls = getCurrentSettings()
local controlName = names[control + 1] or '(no name)'
print("OnStrip: " .. control .. " -> " .. controlName .. "=" .. changeNames[change + 1])
if currentStrip == exampleGUI then
if controlName == 'search' and change == eventClick then -- Click in the combo box
local search = scite.StripValue(1)
print("Searching for " .. search)
elseif controlName == 'ok' and change == eventClick then -- Click on OK
print("Control states: list=" .. scite.StripValue(controls['fruitNames']) ..
", text=" .. scite.StripValue(controls['name']))
scite.StripShow("") -- Hide the dialog
elseif controlName == 'cancel' and change == eventClick then -- Click on Cancel
scite.StripShow("") -- Just hide the dialog
end
elseif currentStrip == testGUI then
if controlName == 'ok' and change == eventClick then -- Click on OK
print("Choices: " .. scite.StripValue(controls['numbers']) ..
" " .. scite.StripValue(controls['days']) ..
" " .. scite.StripValue(controls['seasons']))
scite.StripShow("") -- Hide the dialog
elseif controlName == 'cancel' and change == eventClick then -- Click on Cancel
Yes, I want checkboxes too! :-)
(Radio-buttons can be simulated with combo boxes anyway.)
No, seriously, there is already a nice usable set of components, and most others can be
simulated with them (checkbox = combo box with two elements, just a bit less convenient).
> Yes, I want checkboxes too! :-)
> (Radio-buttons can be simulated with combo boxes anyway.)
>
> No, seriously, there is already a nice usable set of components, and most
> others can be simulated with them (checkbox = combo box with two elements,
> just a bit less convenient).
Checkboxes are used often enough to add. When I was implementing
the find and replace strips though, they took up a lot of room so I
went with iconic buttons instead. For other strips, it may not matter
as much how much space they use.
Neil
Explicitly with function, or follow user with StripSet() and StripSetList()
> Also if first widget (excluding labal) is not input box, pressing Enter does
> not do anything. OTOH if first widget is input box, pressing Enter activates
> next widget (button)
For me, on Xubuntu 11.10, Enter actions the first widget when it is a button.
The code for Enter is in UserStrip::ActivateSignal and is
equivalent to pressing the first button.
> To add more clearance - pressing Enter in input box that is not first
> widget, does not do anything
That hits the same button as hitting Enter in the first input box.
Neil
> The scite.StripSet(1, "...") on a combo box doesn't seem to work.
Fixed now for Windows in Hg. You can also set the text for labels
and buttons but they do not automatically resize.
> Ah, while modifying the script, I found a strange bug: I can type in the
> main SciTE buffer, but when I hit Return, the strip eats the event! I get
> the event displayed in the output pane, and no newline in edition buffer...
Fixed.
> But then focus is lost somewhere outside of the buffers (typing after hitting
> Escape does nothing, until I click in the edit frame).
Now focus goes to edit pane.
> Also a funny bug: if I call StripShow (adults only?) twice with non-empty
> parameters (I assign a shortcut key to the function showing it), the second
> time, the strip content is pushed on the left, the controls are stacked on
> the first third of the strip...
It was only laying out for a resize. Added layout when switching.
That leads to double layout sometimes but probably not important.
> I accidentally called scite.StripShow("testGUI") and it crashes (closed by Windows!) SciTE..
Division by 0 when calculating sizes and no resizable columns. Fixed.
Neil
> ...or focus on first inputbox, instead button
For your example with the (+) and (-) buttons for editing history,
I'd think they belong after the combo as they are commands on the
combo.
Neil
> The reason I moved them on the left side, was because if they are at the
> right side (where they belong, and where I first put them) then if I type
> command on inputbox and hit Enter, the button next to inputbox is executed,
> so it's same problem.
The standard UI for this in dialogs is to specify a default push
button. This used to be drawn distinctively but for GTK+ 3.x, it
appears that this is being de-emphasized. Perhaps double brackets for
the default button ((OK)).
Neil
> I played with the concept. I like the compact textual format, but the usage
> of special symbols and numbers (with off-by-one indexes) isn't very
> practical.
> As an exercise, I made a new format for the GUI: it is parsed into the
> compact format.
Names may be better than numbers. Lua-style indexing from 1
shouldn't matter much as most of Scintilla indexes from 0.
Its easier to write C++ code to parse some text than to read from
Lua structures.
> It allows to name components (last field).
> And the combo boxes are automatically filled.
If you are relying on Lua structures than you can just use a
literal table { "ichi", "ni", "san" } and not worry about "\n"
delimiters. Then its easy to use existing tables of strings.
Neil
Perhaps double brackets for the default button ((OK)).
>> Perhaps double brackets for the default button ((OK)).
>
>
> That behaves strange here.
It was a proposal - no one has implemented it.
Neil
All tested OK.
scite.StripShow(" This is a message in a strip") displays the message in the strip, which
can be a way to display information alternative to the status bar... :-) The first char is
eaten.
Even something silly like:
scite.StripShow("'A'\n'B'\n'C'\n'D'\n'E'\n'F'\n'G'\n'H'\n'I'\n'J'\n'K'\n'L'\n'M'\n'N'\n'O'\n'P'\n'Q'\n'R'\n'S'\n'T'\n'U'\n'V'\n'W'\n'X'\n'Y'\n'Z'\n!")
is displayed rather correctly. The strip isn't displayed if there is no room in the
window. Unlikely to happen in real world cases...
> The standard UI for this in dialogs is to specify a default push
> button. This used to be drawn distinctively but for GTK+ 3.x, it
> appears that this is being de-emphasized. Perhaps double brackets for
> the default button ((OK)).
The ((Default)) button syntax is now working on all three platforms.
Neil