diff --git a/share/grafx2/scripts/_tst_dialog2.lua b/share/grafx2/scripts/_tst_dialog2.lua index de7708ea..3f0b45d7 100644 --- a/share/grafx2/scripts/_tst_dialog2.lua +++ b/share/grafx2/scripts/_tst_dialog2.lua @@ -12,12 +12,10 @@ local form = gui.dialog{ gui.button{ label="+", x=6, y=38, w=14, h=14, repeatable=true, click=function() counter.value=counter.value+1; - counter:render(); end}, gui.button{ label="-", x=26, y=38, w=14, h=14, repeatable=true, click=function() counter.value=counter.value-1; - counter:render(); end}, gui.button{ label="Help", x=6, y=70, w=54, h=14, click=function() @@ -27,6 +25,23 @@ local form = gui.dialog{ x=6, y=18, w=54, h=14, key=27, click=function() return true; -- causes closing end}, + gui.textbox{ + x=6, y=90, nbchar=8, decimal=1, + min=450, max=1450, maxchar=8, value = 1234, + change=function() + -- do nothing + end + }, + gui.textbox{ + x=6, y=104, nbchar=10, maxchar=20, value = "test" + }, + gui.textbox{ + x=6, y=118, nbchar=8, decimal=0, min=0, + maxchar=8, value = 456, + change=function() + -- do nothing + end + }, } form:run() diff --git a/share/grafx2/scripts/libs/gui.lua b/share/grafx2/scripts/libs/gui.lua index 00b48364..247fdc4b 100644 --- a/share/grafx2/scripts/libs/gui.lua +++ b/share/grafx2/scripts/libs/gui.lua @@ -6,7 +6,40 @@ gui = { -- --- dialog() -- +-- colors +-- +black = 0, +dark = 1, +light = 2, +white = 3, + +-- "do nothing" function. Used as default callback +donothing = function(self) +end, + +-- Metatable that hides the field "value" behind a property, +-- and calls render() after it's set. +propvalue = { + __newindex = function(self, index, value) + if index == "value" then + self._value = value + -- extra processing + self:render() + else + rawset(self, index, value) + end + end, + __index = function(self, index ) + if index == "value" then + return self._value + else + return rawget( self, index ) + end + end +}, + +-- +-- dialog() -- dialog = function(args) local dia = { @@ -22,21 +55,26 @@ dialog = function(args) -- -- dialog.run() -- -- - run = function(this) - windowopen(this.w,this.h, this.title or ""); + run = function(self) + windowopen(self.w,self.h, self.title or ""); -- examine all elements - for _,widget in ipairs(this.widgets) do - widget:render() + for _,widget in ipairs(self.widgets) do + widget:create() end repeat local button, button2, key = windowdodialog(); if button > 0 then - local c = this.callbacks[button] - -- run the callback and stop the form if it returns true - if c ~= nil and c(this) then - break; + local c = self.callbacks[button] + if c ~= nil then + -- run the callback + local retvalue = c:click() + -- stop the form if it returns non-nil + if retvalue ~= nil then + windowclose(); + return retvalue; + end end end until key == 27; @@ -51,7 +89,7 @@ dialog = function(args) table.insert(dia.widgets, value) -- clickable widgets take up an auto-numbered id if (value.click) then - dia.callbacks[id] = value.click + dia.callbacks[id] = value id=id+1 end end @@ -60,7 +98,7 @@ dialog = function(args) end, -- --- button() -- +-- button() -- button = function(args) local but = { @@ -70,42 +108,118 @@ button = function(args) h = args.h, key = args.key, label = args.label, - click = args.click or donothing, - render = args.repeatable and function(this) - windowrepeatbutton(this.x, this.y, this.w, this.h, this.label, this.key or -1); + click = args.click or gui.donothing, + create = args.repeatable and function(self) + windowrepeatbutton(self.x, self.y, self.w, self.h, self.label, self.key or -1); end - or function(this) - windowbutton(this.x, this.y, this.w, this.h, this.label, this.key or -1); + or function(self) + windowbutton(self.x, self.y, self.w, self.h, self.label, self.key or -1); end } return but; end, -- --- label() -- +-- label() -- label = function(args) - local fld = { + local lbl = { x = args.x, y = args.y, - value = args.value, + _value = args.value, format = args.format, - fg = args.fg or 0, - bg = args.bg or 2, - render = function(this) - if type(this.format) then - windowprint(this.x, this.y, string.format(this.format, this.value), this.fg, this.bg); + fg = args.fg or gui.black, + bg = args.bg or gui.light, + render = function(self) + if type(self.format) then + windowprint(self.x, self.y, string.format(self.format, self._value), self.fg, self.bg); else - windowprint(this.x, this.y, this.value, this.fg, this.bg); + windowprint(self.x, self.y, self._value, self.fg, self.bg); end - end + end, } - return fld; + lbl.create = lbl.render + setmetatable(lbl, gui.propvalue) + return lbl; end, - --- "do nothing" function. Used as default callback -donothing = function(this) +-- +-- textbox +-- +textbox = function(args) + local txtbox = { + x = args.x, + y = args.y, + nbchar = args.nbchar, -- visible size in characters + --format = args.format, -- numeric, decimal, path + decimal = args.decimal or 0, + min = args.min, + max = args.max, + maxchar = args.maxchar, -- internal size + _value = args.value, + change = args.change or gui.donothing, + --fg = args.fg or gui.black, + --bg = args.bg or gui.light, + create = function(self) + windowinput(self.x, self.y, self.nbchar) + self:render() + end, + render = function(self) + local val = tostring(self._value) + if string.len(val) < self.nbchar then + val = string.rep(" ",self.nbchar - string.len(val)) .. val; + elseif string.len(val) > self.nbchar then + val = string.sub(val, 1, self.nbchar-1) .. gui.char.ellipsis + end + windowprint(self.x, self.y, val, gui.black, gui.light); + end, + click = function(self) + local inputtype + if (type(self._value) == "number" and ((self.min ~= nil and self.min<0) or self.decimal > 0)) then + inputtype = 3 -- entry as double + elseif (type(self._value) == "number") then + inputtype = 1 -- entry as unsigned int + else + inputtype = 0 -- entry as string + end + local accept, val = windowreadline(self.x, self.y, self._value, self.nbchar, self.maxchar, self.decimal, inputtype); + + if accept then + if (inputtype == 1 or inputtype == 3) then + val = tonumber(val) + -- round the decimal places + val = gui.round(val, self.decimal) + end + if (self.min ~= nil and val < self.min) then + val = self.min + end + if (self.max ~= nil and val > self.max) then + val = self.max + end + + self._value = val + end + self:render() + end + } + setmetatable(txtbox, gui.propvalue) + return txtbox; end } + +gui.round = function(val, ipt) + local mult = 10^ipt + return math.floor(val * mult + 0.5) / mult +end + +-- Character constants. May be useful in screens +gui.char = { + ellipsis = string.char(133), -- ... + arrowup = string.char(24), + arrowdown = string.char(25), + arrowleft = string.char(27), + arrowright = string.char(26), + vertical = string.char(18), -- double-ended arrow + horizontal = string.char(29) -- double-ended arrow +} \ No newline at end of file diff --git a/src/factory.c b/src/factory.c index ba8c85a6..cf45c306 100644 --- a/src/factory.c +++ b/src/factory.c @@ -1606,7 +1606,7 @@ int L_WindowInput(lua_State* L) int L_WindowReadline(lua_State* L) { - int x, y, nbchar, maxchar; + int x, y, visible_size, max_size, decimal_places=0; const char *valuetext; double valuenumber; char text[255+1]=""; @@ -1616,31 +1616,29 @@ int L_WindowReadline(lua_State* L) LUA_ARG_NUMBER(1, "windowreadline", x, 3, Window_width-3-8); LUA_ARG_NUMBER(2, "windowreadline", y, 3, Window_height-3-8); - // arg 3 can be either string or number + // arg 3 can be either string or number, it's checked below if (nb_args < (3)) return luaL_error(L, "%s: Argument %d is missing.", "windowreadline", (3)); - if (lua_isnumber(L, (3))) + LUA_ARG_NUMBER(4, "windowreadline", visible_size, 1, (Window_height-3-8)/8); + LUA_ARG_NUMBER(5, "windowreadline", max_size, 1, 255); + LUA_ARG_NUMBER(6, "windowreadline", decimal_places, 0, 16); + LUA_ARG_NUMBER(7, "windowreadline", input_type, 0, 4); + LUA_ARG_LIMIT (7, "windowreadline"); + + if (input_type == 3 || input_type == 1) { - valuenumber = lua_tonumber(L, (3)); - sprintf(text, "%Lf", valuenumber); - input_type=3; + // expect number + LUA_ARG_NUMBER(3, "windowreadline", valuenumber, -DBL_MAX, DBL_MAX); + Sprint_double(text, valuenumber, decimal_places, 0); } - else if (lua_isstring(L, (3))) + else { - valuetext = lua_tostring(L, (3)); + // expect string + LUA_ARG_STRING(3, "windowreadline", valuetext); if (strlen(valuetext)>255) return luaL_error(L, "%s: Argument %d, string too long.", "windowreadline", (3)); strcpy(text, valuetext); } - else - return luaL_error(L, "%s: Argument %d is neither a number nor a string.", "windowreadline", (3)); - LUA_ARG_NUMBER(4, "windowreadline", nbchar, 1, (Window_height-3-8)/8); - maxchar = nbchar; - if (nb_args >= 5) - { - LUA_ARG_NUMBER(5, "windowreadline", maxchar, 1, 255); - LUA_ARG_LIMIT (5, "windowreadline"); - } - + if (!Windows_open) return luaL_error(L, "windowreadline: No window is open"); @@ -1649,10 +1647,10 @@ int L_WindowReadline(lua_State* L) Hide_cursor(); Cursor_is_visible=0; } - result = Readline_ex(x, y, text, nbchar, maxchar, input_type, 0); + result = Readline_ex(x, y, text, visible_size, max_size, input_type, decimal_places); Window_needs_update=1; - lua_pushinteger(L, result); // 0=ESC, 1= confirm + lua_pushboolean(L, result); // 0=ESC, 1= confirm lua_pushstring(L, text); return 2; } @@ -2280,7 +2278,10 @@ void Run_script(const char *script_subdirectory, const char *script_filename) } // Clean up any remaining dialog windows while (Windows_open) + { Close_window(); + Display_cursor(); + } } // Cleanup free(Brush_backup);