diff --git a/API.md b/API.md index 8fbca7d..56bdc4d 100644 --- a/API.md +++ b/API.md @@ -21,26 +21,26 @@ - `laptop.os_get(pos)` - Get an OS object. Usefull in on_construct or on_punch to initialize or do anything with OS - Needed in on_receive_fields to be able to call os:receive_fields(fields, sender) for interactive apps + Needed in on_receive_fields to be able to call mtos:receive_fields(fields, sender) for interactive apps - `laptop.after_place_node` / `laptop.after_dig_node` - (optional) can be used directly for node definition. Move laptop apps data to ItemStack if digged and restored back if placed again. So you can take along your laptop. Note: you need to set `stack_max = 1` because the data can be stored per stack only, not per item. ## Operating system calls Usable from node functions, from apps or outsite -`local os = laptop.os_get(pos)` - Get the Operating system object. pos is the node position +`local mtos = laptop.os_get(pos)` - Get the Operating system object. pos is the node position -- `os:power_on(new_node_name)` - Activate the app "launcher" and if given swap node to new_node_name -- `os:resume(new_node_name)` - Restore the last running app after power_off. if given swap node to new_node_name -- `os:power_off(new_node_name)` - Remove the formspec and if given swap node to new_node_name -- `os:swap_node(new_node_name)`- Swap the node only without any changes on OS -- `os:set_infotext(infotext)` - Set the mouseover infotext for laptop node -- `os:save()` - Store all app-data to nodemeta. Called mostly internally so no explicit call necessary -- `os:get_app(appname)`- Get the app instance -- `os:set_app(appname)` - Start/Enable/navigate to appname. If no appname given the launcher is called -- `os:receive_fields(fields, sender)` - Should be called from node.on_receive_fields to get the apps interactive -- `os:get_theme(theme)`- Get theme data current or requested (theme parameter is optional) -- `os:set_theme(theme)`- Activate theme +- `mtos:power_on(new_node_name)` - Activate the app "launcher" and if given swap node to new_node_name +- `mtos:resume(new_node_name)` - Restore the last running app after power_off. if given swap node to new_node_name +- `mtos:power_off(new_node_name)` - Remove the formspec and if given swap node to new_node_name +- `mtos:swap_node(new_node_name)`- Swap the node only without any changes on OS +- `mtos:set_infotext(infotext)` - Set the mouseover infotext for laptop node +- `mtos:save()` - Store all app-data to nodemeta. Called mostly internally so no explicit call necessary +- `mtos:get_app(appname)`- Get the app instance +- `mtos:set_app(appname)` - Start/Enable/navigate to appname. If no appname given the launcher is called +- `mtos:receive_fields(fields, sender)` - Should be called from node.on_receive_fields to get the apps interactive +- `mtos:get_theme(theme)`- Get theme data current or requested (theme parameter is optional) +- `mtos:set_theme(theme)`- Activate theme ## App Definition @@ -50,14 +50,14 @@ Usable from node functions, from apps or outsite - `app_info` - Short app info visible in launcher tooltip - `fullscreen` - (boolean) Do not add app-background and window buttons - `view` - (boolean) The definition is a view. That means the app/view is not visible in launcher -- `formspec_func(app, os)` - Function, should return the app formspec (mandatory) During definition the "app" and the "os" are available -- `appwindow_formspec_func(launcher_app, app, os)`- Only custom launcher app: App background / Window decorations and buttons -- `receive_fields_func(app, os, fields, sender)` Function for input processing. The "app" and the "os" are available inside the call +- `formspec_func(app, mtos)` - Function, should return the app formspec (mandatory) During definition the "app" and the "mtos" are available +- `appwindow_formspec_func(launcher_app, app, mtos)`- Only custom launcher app: App background / Window decorations and buttons +- `receive_fields_func(app, mtos, fields, sender)` Function for input processing. The "app" and the "mtos" are available inside the call `laptop.register_view(internal_shortname, { definitiontable })` - add a new app or view same as register_app, but the view flag is set. app_name and app_icon not necessary ## App Object -`local app = os:get_app(appname)` - Give the app object internal_shortname, connected to given os. Not necessary in formspec_func or receive_fields_func because given trough interface +`local app = mtos:get_app(appname)` - Give the app object internal_shortname, connected to given mtos. Not necessary in formspec_func or receive_fields_func because given trough interface - `data = app:get_storage_ref(appname)` - Returns a "persitant" data table from nodemeta (=hdd). The data in this table is not lost between formspec_func, receive_fields_func, apps-switch or on/off. Appname is optional to get data from other app - `data = app:get_cloud_storage_ref(appname)` - Returns a persistant table from modmeta (=internet) - `app:back_app() - Go back to previous app/view @@ -72,3 +72,9 @@ Definitiontable: - `exit_button` Exit button image - `major_button` Major (highlighted) button image - `minor_button` Minor button image +- `textcolor` Default text color for buttons and labels. For buttons the major_textcolor and minor_textcolor supported + +## Theme methods +`function laptop.get_theme(theme_name)` +- `theme:get_button(area, prefix, code, text)` get a themed [prefix]_button in area 'x,y;w,h' with code an text +- `theme:get_label(pos, text)` get a themed label text starting at pos 'x,y' diff --git a/apps/calculator_app.lua b/apps/calculator_app.lua new file mode 100644 index 0000000..4bb9585 --- /dev/null +++ b/apps/calculator_app.lua @@ -0,0 +1,133 @@ +laptop.register_app("calculator", { + app_name = "Calculator", + app_icon = "laptop_calculator.png", + app_info = "Calculate things", + formspec_func = function(app, mtos) + local data = app:get_storage_ref() + + if not data.tab then + data.tab = {} + end + if not data.tab[1] then + table.insert(data.tab, {}) + end + + local formspec = "tablecolumns[" .. + "text,align=right,padding=1.5,width=10;".. -- first value + "text,align=right,padding=1.5;".. -- operator + "text,align=right,padding=1.5,width=10]".. -- last value + "table[3.9,0.8;7,2;tab;" + + for idx,entry in ipairs(data.tab) do + if idx > 1 then + formspec = formspec..',' + end + formspec = formspec..(entry.var1 or "")..","..(entry.operator or "")..","..(entry.var2 or "0") + end + + formspec = formspec .. ";"..#data.tab.."]".. + mtos.theme:get_button('4,3;1,1', "minor", 'number', '1') .. + mtos.theme:get_button('5,3;1,1', "minor", 'number', '2') .. + mtos.theme:get_button('6,3;1,1', "minor", 'number', '3') .. + mtos.theme:get_button('4,4;1,1', "minor", 'number', '4') .. + mtos.theme:get_button('5,4;1,1', "minor", 'number', '5') .. + mtos.theme:get_button('6,4;1,1', "minor", 'number', '6') .. + mtos.theme:get_button('4,5;1,1', "minor", 'number', '7') .. + mtos.theme:get_button('5,5;1,1', "minor", 'number', '8') .. + mtos.theme:get_button('6,5;1,1', "minor", 'number', '9') .. + mtos.theme:get_button('4,6;1,1', "minor", 'number', '0') .. + mtos.theme:get_button('5,6;1,1', "minor", 'number', '.') .. + + mtos.theme:get_button('8,3;1,1', "minor", 'operator', '+') .. + mtos.theme:get_button('8,4;1,1', "minor", 'operator', '-') .. + mtos.theme:get_button('8,5;1,1', "minor", 'operator', '/') .. + mtos.theme:get_button('8,6;1,1', "minor", 'operator', '*') .. + mtos.theme:get_button('9,6;2,1', "minor", 'operator', '=') .. + + mtos.theme:get_button('9,3;2,1', "minor", 'del_char', 'DEL-1') .. + mtos.theme:get_button('9,4;2,1', "minor", 'del_line', 'DEL-L') .. + mtos.theme:get_button('9,5;2,1', "minor", 'del_all', 'DEL-A') + return formspec + end, + + receive_fields_func = function(app, mtos, fields, sender) + local data = app:get_storage_ref() + local entry = data.tab[#data.tab] + + if fields.number then + -- simple number entry + entry.var2 = (entry.var2 or "")..minetest.strip_colors(fields.number) + elseif fields.del_char then + -- delete char + if entry.var2 and entry.var2 ~= "" then + -- remove char from current number + entry.var2 = entry.var2:sub(1, -2) + if entry.var2 == "" then + entry.var2 = nil + end + else + -- get previous number + if #data.tab > 1 then + -- go back to previous line if exists + table.remove(data.tab, #data.tab) + else + -- get from left site if first entry + entry.var2 = entry.var1 + entry.operator = nil + entry.var1 = nil + end + end + elseif fields.del_line then + -- just delete full number if exists + if entry.var2 and entry.var2 ~= "" then + entry.var2 = nil + else + -- go back to previous line and delete the full number if exists + table.remove(data.tab, #data.tab) + if #data.tab > 0 then + entry = data.tab[#data.tab] + entry.var2 = nil + end + end + elseif fields.del_all then + data.tab = nil + elseif fields.operator then + fields.operator = minetest.strip_colors(fields.operator) + local entry = data.tab[#data.tab] + -- no previous operator + if not entry.operator then + if fields.operator == '=' then + table.insert(data.tab, {}) -- add empty line + elseif entry.var2 and entry.var2 ~= "" then + -- move to the left + entry.var1 = entry.var2 + entry.operator = fields.operator + entry.var2 = nil + end + + -- process previous operator + else + local result + if entry.operator == '+' then + result = tonumber(entry.var1) + tonumber(entry.var2) + elseif entry.operator == '-' then + result = tonumber(entry.var1) - tonumber(entry.var2) + elseif entry.operator == '/' then + result = tonumber(entry.var1) / tonumber(entry.var2) + elseif entry.operator == '*' then + result = tonumber(entry.var1) * tonumber(entry.var2) + elseif entry.operator == '=' then + result = tonumber(entry.var2) + end + if not result then + result = 0 + end + if fields.operator == '=' then + table.insert(data.tab, {var2 = tostring(result)}) + else + table.insert(data.tab, {var1 = tostring(result), operator = fields.operator}) + end + end + end + end +}) diff --git a/apps/launcher_app.lua b/apps/launcher_app.lua index ef47074..230fad7 100644 --- a/apps/launcher_app.lua +++ b/apps/launcher_app.lua @@ -2,13 +2,13 @@ laptop.register_app("launcher", { -- app_name = "Main launcher", -- not in launcher list fullscreen = true, - formspec_func = function(launcher_app, os) + formspec_func = function(launcher_app, mtos) local c_row_count = 4 local i = 0 local out = "size[15,10]" - if os.theme.launcher_bg then - out = out..'background[15,10;0,0;'..os.theme.launcher_bg..';true]' + if mtos.theme.launcher_bg then + out = out..'background[15,10;0,0;'..mtos.theme.launcher_bg..';true]' end local appslist_sorted = {} for name, def in pairs(laptop.apps) do @@ -20,34 +20,35 @@ laptop.register_app("launcher", { for i, e in ipairs(appslist_sorted) do local x = math.floor((i-1) / c_row_count)*2 + 1 local y = ((i-1) % c_row_count)*2 + 1 - out = out .. 'image_button['..x..','..y..';1,1;'..os.theme.app_button..'^'..(e.def.app_icon or 'logo.png')..';'..e.name..';]'.. + out = out .. 'image_button['..x..','..y..';1,1;'..mtos.theme.app_button..'^'..(e.def.app_icon or 'logo.png')..';'..e.name..';]'.. 'label['..(x-0.3)..','..(y+1)..';'..e.def.app_name..']'.. 'tooltip['..e.name..';'..(e.def.app_info or e.name)..']' --;;]' end + out = out..mtos.theme:get_button("11,9.8;4,0.7", "major", "os_clock", os.date("%c")) return out end, - appwindow_formspec_func = function(launcher_app, app, os) + appwindow_formspec_func = function(launcher_app, app, mtos) local formspec = 'size[15,10]' - if os.theme.app_bg then - formspec = formspec..'background[0,0;15,10;'..os.theme.app_bg..';true]' + if mtos.theme.app_bg then + formspec = formspec..'background[0,0;15,10;'..mtos.theme.app_bg..';true]' end - if #os.appdata.os.stack > 0 then - formspec = formspec..'image_button[-0.29,-0.31;1.09,0.61;'..os.theme.back_button..';os_back;<]' --TODO: if stack exists + if #mtos.appdata.os.stack > 0 then + formspec = formspec..'image_button[-0.29,-0.31;1.09,0.61;'..mtos.theme.back_button..';os_back;<]' end if app.app_info then - if #os.appdata.os.stack > 0 then + if #mtos.appdata.os.stack > 0 then formspec = formspec.."label[0.8,-0.29;"..app.app_info.."]" else formspec = formspec.."label[-0.1,-0.29;"..app.app_info.."]" end end - formspec = formspec..'image_button[14.2,-0.31;1.09,0.61;'..os.theme.exit_button..';os_exit;X]' + formspec = formspec..'image_button[14.2,-0.31;1.09,0.61;'..mtos.theme.exit_button..';os_exit;X]' return formspec end, - receive_fields_func = function(launcher_app, os, fields, sender) + receive_fields_func = function(launcher_app, mtos, fields, sender) for name, descr in pairs(fields) do if laptop.apps[name] then - os:set_app(name) + mtos:set_app(name) break end end diff --git a/apps/launcher_settings_app.lua b/apps/launcher_settings_app.lua index f0ebfb0..1a8d03d 100644 --- a/apps/launcher_settings_app.lua +++ b/apps/launcher_settings_app.lua @@ -12,15 +12,15 @@ laptop.register_app("launcher_settings", { app_icon = "laptop_setting_wrench.png", app_info = "Change the computer's settings.", - formspec_func = function(app, os) + formspec_func = function(app, mtos) local settings_data = app:get_storage_ref() -- Change background setting - local current_theme_name = settings_data.selected_theme or os:get_theme().name or "default" - local current_theme = os:get_theme(current_theme_name) + local current_theme_name = settings_data.selected_theme or mtos:get_theme().name or "default" + local current_theme = mtos:get_theme(current_theme_name) local current_idx - local formspec = "label[0,0.5;Select theme]" + local formspec = mtos.theme:get_label('0,0.5', "Select theme") local formspec = formspec.."textlist[0,1;5,2;sel_theme;" for i, theme in ipairs(themes_tab) do @@ -41,12 +41,12 @@ laptop.register_app("launcher_settings", { formspec = formspec.."image[5.5,1;5,3.75;"..current_theme.launcher_bg.."]" end - formspec = formspec..'image_button[-0.14,3;3,1;'..current_theme.major_button..';theme_apply;Apply]' + formspec = formspec .. mtos.theme:get_button('0,3.2;2.5,0.6', 'major', 'theme_apply', 'Apply', 'Apply theme') return formspec end, - receive_fields_func = function(app, os, fields, sender) + receive_fields_func = function(app, mtos, fields, sender) local settings_data = app:get_storage_ref() if fields.sel_theme then @@ -56,9 +56,8 @@ laptop.register_app("launcher_settings", { end if fields.theme_apply and settings_data.selected_theme then - os:set_theme(settings_data.selected_theme) + mtos:set_theme(settings_data.selected_theme) settings_data.selected_theme = nil - app:exit_app() end end }) diff --git a/apps/mail_app.lua b/apps/mail_app.lua new file mode 100644 index 0000000..63e5d57 --- /dev/null +++ b/apps/mail_app.lua @@ -0,0 +1,249 @@ +-- based on https://github.com/cheapie/mail + +laptop.register_app("mail", { + app_name = "Mail", + app_icon = "laptop_email_letter.png", + app_info = "Write mails to other players", + formspec_func = function(app, mtos) + local cloud = app:get_cloud_storage_ref("mail") + if not mtos.appdata.os.last_player then + mtos:set_app() -- no player. Back to launcher + return false + end + + if not cloud[mtos.appdata.os.last_player] then + mtos:set_app("mail:newplayer") + return false + end + local account = cloud[mtos.appdata.os.last_player] + account.selected_box = account.selected_box or "inbox" + account.selected_index = nil -- will be new determinated by selectedmessage + local box = account[account.selected_box] -- inbox or outbox + + local formspec = + "label[4,-0.31;Welcome "..mtos.appdata.os.last_player.."]".. + "tablecolumns[" .. + "image,align=center,1=laptop_mail.png,2=laptop_mail_read.png;".. --icon column + "color;".. -- subject and date color + "text;".. -- subject + "text,padding=1.5;".. -- sender + "text,padding=1.5,align=right]".. -- date + "table[0,0.5;7.5,8.2;message;" + + if box and box[1] then + for idx,message in ipairs(box) do + if idx > 1 then + formspec = formspec..',' + end + -- set read/unread status + if account.selected_box == "sentbox" then + formspec = formspec .. "1,#88FF88," -- unread + elseif not message.is_read then + formspec = formspec .. "1,#FF8888," -- unread + else + formspec = formspec .. "2,#FFFFFF," -- read + end + + -- set subject + if not message.subject or message.subject == "" then + formspec = formspec .. "(No subject)," + elseif string.len(message.subject) > 30 then + formspec = formspec .. minetest.formspec_escape(string.sub(message.subject,1,27)) .. "...," + else + formspec = formspec .. minetest.formspec_escape(message.subject) .. "," + end + + -- set sender or receiver + if account.selected_box == "inbox" then + formspec = formspec..minetest.formspec_escape(message.sender or "") .."," -- body + else + formspec = formspec..minetest.formspec_escape(message.receiver or "") .."," -- body + end + + -- set date + formspec = formspec .. os.date("%c", message.time) -- timestamp + + -- handle marked line + if account.selectedmessage and + message.sender == account.selectedmessage.sender and + message.subject == account.selectedmessage.subject and + message.time == account.selectedmessage.time and + message.body == account.selectedmessage.body then + account.selected_index = idx + end + end + formspec = formspec .. ";"..(account.selected_index or "").."]" + else + formspec = formspec .. ",,No mail :(]" + end + + -- toggle inbox/sentbox + if account.selected_box == "inbox" then + formspec = formspec .. mtos.theme:get_button('0,9;1.5,1', 'minor', 'switch_sentbox', 'Sentbox', 'Show sent messages') + else + formspec = formspec .. mtos.theme:get_button('0,9;1.5,1', 'minor', 'switch_inbox', 'Inbox', 'Show received messages') + end + + formspec = formspec .. "image_button[1.7,9;1,1;"..mtos.theme.minor_button.."^laptop_email_new.png;new;]tooltip[new;New message]" + if account.newmessage then + formspec = formspec .. "image_button[2.7,9;1,1;"..mtos.theme.minor_button.."^laptop_email_edit.png;continue;]tooltip[continue;Continue last message]" + end + + if account.selectedmessage then + formspec = formspec .. + "image_button[3.7,9;1,1;"..mtos.theme.minor_button.."^laptop_email_reply.png;reply;]tooltip[reply;Reply]".. + "image_button[4.7,9;1,1;"..mtos.theme.minor_button.."^laptop_email_forward.png;forward;]tooltip[forward;Forward]".. + "image_button[5.7,9;1,1;"..mtos.theme.minor_button.."^laptop_email_trash.png;delete;]tooltip[delete;Delete]" + if account.selected_box == "inbox" then + if not account.selectedmessage.is_read then + formspec = formspec .. "image_button[6.7,9;1,1;"..mtos.theme.minor_button.."^laptop_mail_read.png;markread;]tooltip[markread;Mark message as read]" + else + formspec = formspec .. "image_button[6.7,9;1,1;"..mtos.theme.minor_button.."^laptop_mail.png;markunread;]tooltip[markunread;Mark message as unread]" + end + end + if account.selected_box == "inbox" then + formspec = formspec .. mtos.theme:get_label('8,0.5', "From: "..(account.selectedmessage.sender or "")) + else + formspec = formspec .. mtos.theme:get_label('8,0.5', "To: "..(account.selectedmessage.receiver or "")) + end + + formspec = formspec .. mtos.theme:get_label('8,1', "Subject: "..(account.selectedmessage.subject or "")).. + "background[8,1.55;6.92,7.3;gui_formbg.png]".. + "textarea[8.25,1.5;7,8.35;body;;"..(minetest.formspec_escape(account.selectedmessage.body) or "").."]" + end + return formspec + end, + receive_fields_func = function(app, mtos, fields, sender) + if sender:get_player_name() ~= mtos.appdata.os.last_player then + mtos:set_app() -- wrong player. Back to launcher + return + end + + local cloud = app:get_cloud_storage_ref("mail") + local account = cloud[mtos.appdata.os.last_player] + if not account then + mtos:set_app() -- wrong player. Back to launcher + return + end + account.selected_box = account.selected_box or "inbox" + local box = account[account.selected_box] -- inbox or outbox + + -- Set read status if 2 seconds selected + if account.selected_index and account.selectedmessage and account.selected_box == "inbox" and + account.selected_timestamp and (os.time() - account.selected_timestamp) > 1 then + account.selectedmessage.is_read = true + end + + -- process input + if fields.message then + local event = minetest.explode_table_event(fields.message) + account.selectedmessage = box[event.row] + if account.selectedmessage then + account.selected_index = event.row + account.selected_timestamp = os.time() + else + account.selected_index = nil + end + elseif fields.new then + account.newmessage = {} + mtos:set_app("mail:compose") + elseif fields.continue then + mtos:set_app("mail:compose") + elseif fields.switch_sentbox then + account.selected_box = "sentbox" + account.selectedmessage = nil + elseif fields.switch_inbox then + account.selected_box = "inbox" + account.selectedmessage = nil + elseif account.selected_index then + if fields.delete then + table.remove(box, account.selected_index) + account.selectedmessage = nil + elseif fields.reply then + account.newmessage = {} + account.newmessage.receiver = account.selectedmessage.sender + account.newmessage.subject = "Re: "..(account.selectedmessage.subject or "") + account.newmessage.body = "Type your reply here."..string.char(10)..string.char(10).."--Original message follows--"..string.char(10)..(account.selectedmessage.body or "") + mtos:set_app("mail:compose") + elseif fields.forward then + account.newmessage = {} + account.newmessage.subject = "Fw: "..(account.selectedmessage.subject or "") + account.newmessage.body = "Type your reply here."..string.char(10)..string.char(10).."--Original message follows--"..string.char(10)..(account.selectedmessage.body or "") + mtos:set_app("mail:compose") + elseif fields.markread then + account.selectedmessage.is_read = true + elseif fields.markunread then + account.selectedmessage.is_read = false + account.selected_timestamp = nil -- Stop timer + end + end + end +}) + +laptop.register_view("mail:newplayer", { + formspec_func = function(app, mtos) + return mtos.theme:get_label('1,3', "No mail account for player "..mtos.appdata.os.last_player.. " found. Do you like to create a new account?").. + mtos.theme:get_button('1,4;3,1', 'major', 'create', 'Create account') + end, + receive_fields_func = function(app, mtos, fields, sender) + if sender:get_player_name() ~= mtos.appdata.os.last_player then + mtos:set_app() -- wrong player. Back to launcher + return + end + if fields.create then + local cloud = app:get_cloud_storage_ref("mail") + cloud[mtos.appdata.os.last_player] = { + inbox = {}, + sentbox = {} + } + app:back_app() + elseif fields.os_back then + app:exit_app() + end + end +}) + +-- Write new mail +laptop.register_view("mail:compose", { + formspec_func = function(app, mtos) + local cloud = app:get_cloud_storage_ref("mail") + local account = cloud[mtos.appdata.os.last_player] + account.newmessage = account.newmessage or {} + local message = account.newmessage + + local formspec = "background[-0.1,0.4;4.2,2.4;gui_formbg.png]".. + "field[0.25,1;4,1;receiver;To:;%s]field[0.25,2;4,1;subject;Subject:;%s]".. + "background[0,3.05;7.95,3.44;gui_formbg.png]".. + "textarea[0.25,3;8,4;body;;%s]".. + mtos.theme:get_button("0,8;2,1", "major", "send", "Send message") + formspec = string.format(formspec,minetest.formspec_escape(message.receiver or ""),minetest.formspec_escape(message.subject or ""),minetest.formspec_escape(message.body or "")) + if message.receiver and not cloud[message.receiver] then + formspec = formspec..mtos.theme:get_label('2.3,8', "invalid receiver player") + end + return formspec + end, + receive_fields_func = function(app, mtos, fields, sender) + if sender:get_player_name() ~= mtos.appdata.os.last_player then + mtos:set_app() -- wrong player. Back to launcher + return + end + + local cloud = app:get_cloud_storage_ref("mail") + local account = cloud[mtos.appdata.os.last_player] + account.newmessage = account.newmessage or {} + local message = account.newmessage + + message.receiver = fields.receiver or message.receiver + message.sender = mtos.appdata.os.last_player + message.time = os.time() + message.subject = fields.subject or message.subject + message.body = fields.body or message.body + + if fields.send and message.receiver and cloud[message.receiver] then + table.insert(cloud[message.receiver].inbox, message) + table.insert(account.sentbox, table.copy(message)) + account.newmessage = nil + app:back_app() + end + end +}) diff --git a/apps/stickynote_app.lua b/apps/stickynote_app.lua index b635e7e..85ff5b1 100644 --- a/apps/stickynote_app.lua +++ b/apps/stickynote_app.lua @@ -6,7 +6,8 @@ laptop.register_app("stickynote", { local data = app:get_storage_ref() data.text = data.text or "" - return "textarea[0.35,0.35;15.08,10.5;text;;"..minetest.formspec_escape(data.text).."]" + return "background[0,0.35;15.2,9.2;gui_formbg.png]".. + "textarea[0.35,0.35;15.08,10.5;text;;"..minetest.formspec_escape(data.text).."]" end, receive_fields_func = function(app, os, fields, sender) if fields.text then diff --git a/demo_apps.lua b/demo_apps.lua index 7b76e74..c96f03e 100644 --- a/demo_apps.lua +++ b/demo_apps.lua @@ -3,36 +3,36 @@ laptop.register_app("demo1", { app_name = "Demo App", app_icon = "laptop_setting_wrench.png", app_info = "The first and simple demo app", - formspec_func = function(app, os) - return 'image_button[5,5;3,1;'..os.theme.major_button..';next;Second screen]' + formspec_func = function(app, mtos) + return mtos.theme:get_button('5,5;3,1', 'major', 'next', 'Second screen') end, - receive_fields_func = function(app, os, fields, sender) + receive_fields_func = function(app, mtos, fields, sender) if fields.next then - os:set_app("demo1_view2") + mtos:set_app("demo1_view2") end end }) laptop.register_view("demo1_view2", { app_info = "Second screen in Demo App 1", - formspec_func = function(app, os) - return "label[1,5;Use the framework buttons to navigate back or cancel]" + formspec_func = function(app, mtos) + return mtos.theme:get_label('1,5', "Use the framework buttons to navigate back or cancel") end, - receive_fields_func = function(app, os, fields, sender) + receive_fields_func = function(app, mtos, fields, sender) end }) laptop.register_app("demo2", { app_name = "Demo App 2", - formspec_func = function(app, os) + formspec_func = function(app, mtos) local data = app:get_storage_ref() data.counter = data.counter or 1 return 'button[3,1;5,1;count;Click: '..data.counter..']'.. 'button[3,3;5,1;back;Back to launcher]' end, - receive_fields_func = function(app, os, fields, sender) + receive_fields_func = function(app, mtos, fields, sender) if fields.count then local data = app:get_storage_ref() data.counter = data.counter + 1 diff --git a/node_fw.lua b/node_fw.lua index 451e07a..291052d 100644 --- a/node_fw.lua +++ b/node_fw.lua @@ -4,12 +4,12 @@ laptop.node_config = {} local function after_place_node(pos, placer, itemstack, pointed_thing) local appdata = minetest.deserialize(itemstack:get_meta():get_string("laptop_appdata")) if appdata then - local os = laptop.os_get(pos) - os.appdata = appdata - os.appdata.launcher = os.appdata.launcher or {} - os.appdata.os = os.appdata.os or {} - os.appdata.os.stack = os.appdata.os.stack or {} - os:save() + local mtos = laptop.os_get(pos) + mtos.appdata = appdata + mtos.appdata.launcher = mtos.appdata.launcher or {} + mtos.appdata.os = mtos.appdata.os or {} + mtos.appdata.os.stack = mtos.appdata.os.stack or {} + mtos:save() end end @@ -30,38 +30,38 @@ local function after_dig_node(pos, oldnode, oldmetadata, digger) end local function on_construct(pos) - local os = laptop.os_get(pos) + local mtos = laptop.os_get(pos) local node = minetest.get_node(pos) local hwdef = laptop.node_config[node.name] if hwdef.custom_theme then -- initial only - os:set_theme(hwdef.custom_theme) + mtos:set_theme(hwdef.custom_theme) end if hwdef.hw_state then - os[hwdef.hw_state](os) + mtos[hwdef.hw_state](mtos) else - os:power_off() + mtos:power_off() end - os:set_infotext(hwdef.hw_infotext) + mtos:set_infotext(hwdef.hw_infotext) end local function on_punch(pos, node, puncher) - local os = laptop.os_get(pos) + local mtos = laptop.os_get(pos) local hwdef = laptop.node_config[node.name] if hwdef.next_node then local hwdef_next = laptop.node_config[hwdef.next_node] if hwdef_next.hw_state then - os[hwdef_next.hw_state](os, hwdef.next_node) + mtos[hwdef_next.hw_state](mtos, hwdef.next_node) else - os:swap_node(hwdef.next_node) - os:save() + mtos:swap_node(hwdef.next_node) + mtos:save() end - os:set_infotext(hwdef_next.hw_infotext) + mtos:set_infotext(hwdef_next.hw_infotext) end end local function on_receive_fields(pos, formname, fields, sender) - local os = laptop.os_get(pos) - os:receive_fields(fields, sender) + local mtos = laptop.os_get(pos) + mtos:receive_fields(fields, sender) end function laptop.register_hardware(name, hwdef) diff --git a/nodes.lua b/nodes.lua index dce14a9..4189e01 100644 --- a/nodes.lua +++ b/nodes.lua @@ -73,7 +73,7 @@ laptop.register_hardware("laptop:cube", { description = "CUBE PC", infotext = "CUBE PC", sequence = { "off", "on"}, - custom_theme = "red", + custom_theme = "Red", node_defs = { ["on"] = { hw_state = "power_on", diff --git a/os.lua b/os.lua index d831678..25052fa 100644 --- a/os.lua +++ b/os.lua @@ -58,16 +58,7 @@ end -- Get given or current theme function os_class:get_theme(theme) local theme_sel = theme or self.appdata.os.theme - local ret = table.copy(laptop.themes.default) - if theme_sel and laptop.themes[theme_sel] then - for k,v in pairs(laptop.themes[theme_sel]) do - ret[k] = v - end - ret.name = theme_sel - else - ret.name = "default" - end - return ret + return laptop.get_theme(theme_sel) end -- Set current theme diff --git a/textures/email_edit.png b/textures/laptop_email_edit.png similarity index 100% rename from textures/email_edit.png rename to textures/laptop_email_edit.png diff --git a/textures/email_forward.png b/textures/laptop_email_forward.png similarity index 100% rename from textures/email_forward.png rename to textures/laptop_email_forward.png diff --git a/textures/email_new.png b/textures/laptop_email_new.png similarity index 100% rename from textures/email_new.png rename to textures/laptop_email_new.png diff --git a/textures/email_reply.png b/textures/laptop_email_reply.png similarity index 100% rename from textures/email_reply.png rename to textures/laptop_email_reply.png diff --git a/textures/email_trash.png b/textures/laptop_email_trash.png similarity index 100% rename from textures/email_trash.png rename to textures/laptop_email_trash.png diff --git a/textures/mail.png b/textures/laptop_mail.png similarity index 100% rename from textures/mail.png rename to textures/laptop_mail.png diff --git a/textures/mail_read.png b/textures/laptop_mail_read.png similarity index 100% rename from textures/mail_read.png rename to textures/laptop_mail_read.png diff --git a/themes.lua b/themes.lua index c4ef4f2..10c4884 100644 --- a/themes.lua +++ b/themes.lua @@ -7,6 +7,7 @@ laptop.themes = { back_button = "laptop_theme_freedom_back_button.png", exit_button = "laptop_theme_freedom_exit_button.png", app_button = "laptop_theme_freedom_app_button.png", + textcolor = "#000000", }, } @@ -25,4 +26,30 @@ for _, file in ipairs(theme_list) do if file:sub(-10) == '_theme.lua' then dofile(theme_path..file) end -end \ No newline at end of file +end + +local theme_class = {} +theme_class.__index = theme_class + +-- get prepared button textures +function theme_class:get_button(area, prefix, code, text, tooltip) + return'image_button['..area..';'..self[prefix.."_button"]..';'..code..';'.. minetest.colorize(self[prefix.."_textcolor"] or self.textcolor,minetest.formspec_escape(text))..']'.. + "tooltip["..code..";"..minetest.formspec_escape(tooltip or text).."]" +end +-- Get themed label +function theme_class:get_label(area, label) + return'label['..area..';'..minetest.colorize(self.textcolor, minetest.formspec_escape(label))..']' +end + +function laptop.get_theme(theme_name) + local self = setmetatable(table.copy(laptop.themes.default), theme_class) + if theme_name and laptop.themes[theme_name] then + for k,v in pairs(laptop.themes[theme_name]) do + self[k] = v + end + self.name = theme_name + else + self.name = "default" + end + return self +end diff --git a/themes/basic_theme.lua b/themes/basic_theme.lua index 1b53097..6cef136 100644 --- a/themes/basic_theme.lua +++ b/themes/basic_theme.lua @@ -1,9 +1,10 @@ -laptop.register_theme("Basic", { - launcher_bg = "laptop_theme_basic_launcher_bg.png", - app_bg = "laptop_theme_basic_app_bg.png", - major_button = "laptop_theme_basic_button.png", - minor_button = "laptop_theme_basic_button.png", - back_button = "blank.png", - exit_button = "blank.png", - app_button = "blank.png", -}) \ No newline at end of file +laptop.register_theme("Basic", { + launcher_bg = "laptop_theme_basic_launcher_bg.png", + app_bg = "laptop_theme_basic_app_bg.png", + major_button = "laptop_theme_basic_button.png", + minor_button = "laptop_theme_basic_button.png", + back_button = "blank.png", + exit_button = "blank.png", + app_button = "blank.png", + textcolor = "#FFFFFF", +}) diff --git a/themes/blue_theme.lua b/themes/blue_theme.lua index 21d8f2f..87fc4a4 100644 --- a/themes/blue_theme.lua +++ b/themes/blue_theme.lua @@ -1,9 +1,9 @@ -laptop.register_theme("Blue", { - launcher_bg = "laptop_theme_blue_launcher_bg.png", - app_bg = "laptop_theme_blue_app_bg.png", - major_button = "laptop_theme_blue_major_button.png", - minor_button = "laptop_theme_minor_button.png", - back_button = "laptop_theme_blue_back_button.png", - exit_button = "laptop_theme_blue_exit_button.png", - app_button = "laptop_theme_blue_app_button.png", +laptop.register_theme("Blue", { + launcher_bg = "laptop_theme_blue_launcher_bg.png", + app_bg = "laptop_theme_blue_app_bg.png", + major_button = "laptop_theme_blue_major_button.png", + minor_button = "laptop_theme_minor_button.png", + back_button = "laptop_theme_blue_back_button.png", + exit_button = "laptop_theme_blue_exit_button.png", + app_button = "laptop_theme_blue_app_button.png", }) \ No newline at end of file diff --git a/themes/cubic_theme.lua b/themes/cubic_theme.lua index 57637e1..18b5a3d 100644 --- a/themes/cubic_theme.lua +++ b/themes/cubic_theme.lua @@ -1,9 +1,9 @@ -laptop.register_theme("Cubic", { - launcher_bg = "laptop_theme_cubic_launcher_bg.png", - app_bg = "laptop_theme_cubic_app_bg.png", - major_button = "laptop_theme_cubic_major_button.png", - minor_button = "laptop_theme_minor_button.png", - back_button = "laptop_theme_cubic_back_button.png", - exit_button = "laptop_theme_cubic_exit_button.png", - app_button = "laptop_theme_cubic_app_button.png", +laptop.register_theme("Cubic", { + launcher_bg = "laptop_theme_cubic_launcher_bg.png", + app_bg = "laptop_theme_cubic_app_bg.png", + major_button = "laptop_theme_cubic_major_button.png", + minor_button = "laptop_theme_minor_button.png", + back_button = "laptop_theme_cubic_back_button.png", + exit_button = "laptop_theme_cubic_exit_button.png", + app_button = "laptop_theme_cubic_app_button.png", }) \ No newline at end of file diff --git a/themes/magma_theme.lua b/themes/magma_theme.lua index 497626f..b5570c6 100644 --- a/themes/magma_theme.lua +++ b/themes/magma_theme.lua @@ -1,9 +1,9 @@ -laptop.register_theme("Magma", { - launcher_bg = "laptop_theme_magma_launcher_bg.png", - app_bg = "laptop_theme_magma_app_bg.png", - major_button = "laptop_theme_magma_major_button.png", - minor_button = "laptop_theme_minor_button.png", - back_button = "laptop_theme_magma_back_button.png", - exit_button = "laptop_theme_magma_exit_button.png", - app_button = "laptop_theme_magma_app_button.png", +laptop.register_theme("Magma", { + launcher_bg = "laptop_theme_magma_launcher_bg.png", + app_bg = "laptop_theme_magma_app_bg.png", + major_button = "laptop_theme_magma_major_button.png", + minor_button = "laptop_theme_minor_button.png", + back_button = "laptop_theme_magma_back_button.png", + exit_button = "laptop_theme_magma_exit_button.png", + app_button = "laptop_theme_magma_app_button.png", }) \ No newline at end of file diff --git a/themes/red_theme.lua b/themes/red_theme.lua index 0a6c4c6..b435dcd 100644 --- a/themes/red_theme.lua +++ b/themes/red_theme.lua @@ -1,9 +1,9 @@ -laptop.register_theme("Red", { - launcher_bg = "laptop_theme_red_launcher_bg.png", - app_bg = "laptop_theme_red_app_bg.png", - major_button = "laptop_theme_red_major_button.png", - minor_button = "laptop_theme_minor_button.png", - back_button = "laptop_theme_red_back_button.png", - exit_button = "laptop_theme_red_exit_button.png", - app_button = "laptop_theme_red_app_button.png", +laptop.register_theme("Red", { + launcher_bg = "laptop_theme_red_launcher_bg.png", + app_bg = "laptop_theme_red_app_bg.png", + major_button = "laptop_theme_red_major_button.png", + minor_button = "laptop_theme_minor_button.png", + back_button = "laptop_theme_red_back_button.png", + exit_button = "laptop_theme_red_exit_button.png", + app_button = "laptop_theme_red_app_button.png", }) \ No newline at end of file