Add screenshots and a README to display them.

This commit is contained in:
Signal 2026-02-15 19:20:34 -05:00
parent ce6cb893bf
commit 48ffa74bfd
12 changed files with 229 additions and 168 deletions

26
README.md Normal file
View file

@ -0,0 +1,26 @@
# Screenshots
- Meta menu:
![Meta menu](screenshots/meta.png)
- Server list:
![Servers menu](screenshots/servers.png)
- Content menu:
![Content menu](screenshots/content.png)
- ContentDB menu:
![ContentDB menu](screenshots/contentdb.png)
- Settings menu:
![Settings menu](screenshots/settings.png)
- Credits menu:
![Credits menu](screenshots/credits.png)

View file

@ -373,7 +373,7 @@ fs_hypertext.__index = fs_hypertext
setmetatable(fs_hypertext, { setmetatable(fs_hypertext, {
__call = function(_, x, y, w, h, txt) __call = function(_, x, y, w, h, txt)
local e = {x = x, y = y, w = w, h = h, txt = txt, _styles = {}} local e = {x = x, y = y, w = w, h = h, txt = txt, _styles = {}}
e.__id = "_"..minetest.get_us_time().."_"..math.random(1, 100000) e.__id = new_id()
setmetatable(e, fs_hypertext) setmetatable(e, fs_hypertext)
table.insert(ctx, e) table.insert(ctx, e)
return e return e
@ -747,7 +747,7 @@ fs_scrollbar.__index = fs_scrollbar
setmetatable(fs_scrollbar, { setmetatable(fs_scrollbar, {
__call = function(_, x, y, w, h, orientation, value) __call = function(_, x, y, w, h, orientation, value)
local e = {x = x, y = y, w = w, h = h, orientation = orientation or "vertical", value = value or "", _styles = {}} local e = {x = x, y = y, w = w, h = h, orientation = orientation or "vertical", value = value or "", _styles = {}}
e.__id = "_"..minetest.get_us_time().."_"..math.random(1, 100000) e.__id = new_id()
setmetatable(e, fs_scrollbar) setmetatable(e, fs_scrollbar)
ctx[#ctx +1] = e ctx[#ctx +1] = e
return e return e

View file

@ -259,7 +259,7 @@ function show_online_content_menu(content)
show_type = imfs.state("all"), show_type = imfs.state("all"),
content_shown = imfs.state(), content_shown = imfs.state(),
page = imfs.state(1), page = imfs.state(1),
current_package = imfs.state(), detail = imfs.state(),
}) })
end end

BIN
screenshots/content.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 104 KiB

BIN
screenshots/contentdb.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 905 KiB

BIN
screenshots/credits.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 181 KiB

BIN
screenshots/meta.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

BIN
screenshots/servers.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 158 KiB

BIN
screenshots/settings.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 144 KiB

View file

@ -35,7 +35,11 @@ return function(state)
imfs.row(0.15, 0.05, "100% - 0.3", "100% - 0.13") imfs.row(0.15, 0.05, "100% - 0.3", "100% - 0.13")
local show_type = state.show_type() local show_type = state.show_type()
imfs.button(0, 0, "1x", "100%", "All") imfs.button(0, 0, "1x", "100%", "All")
:style(show_type == "all" and style_borderless_alt or style_borderless) :style(
show_type == "all"
and style_borderless_alt
or style_borderless
)
:style("hovered", style_borderless_hovered) :style("hovered", style_borderless_hovered)
:style("pressed", style_borderless_hovered) :style("pressed", style_borderless_hovered)
:onclick(function() :onclick(function()
@ -52,7 +56,11 @@ return function(state)
end) end)
imfs.box(0, 0, 0.1, "100%", theme.styles.container.border_color) imfs.box(0, 0, 0.1, "100%", theme.styles.container.border_color)
imfs.button(0, 0, "1x", "100%", "Games") imfs.button(0, 0, "1x", "100%", "Games")
:style(show_type == "games" and style_borderless_alt or style_borderless) :style(
show_type == "games"
and style_borderless_alt
or style_borderless
)
:style("hovered", style_borderless_hovered) :style("hovered", style_borderless_hovered)
:style("pressed", style_borderless_hovered) :style("pressed", style_borderless_hovered)
:onclick(function() :onclick(function()
@ -69,7 +77,11 @@ return function(state)
end) end)
imfs.box(0, 0, 0.1, "100%", theme.styles.container.border_color) imfs.box(0, 0, 0.1, "100%", theme.styles.container.border_color)
imfs.button(0, 0, "1x", "100%", "Mods") imfs.button(0, 0, "1x", "100%", "Mods")
:style(show_type == "mods" and style_borderless_alt or style_borderless) :style(
show_type == "mods"
and style_borderless_alt
or style_borderless
)
:style("hovered", style_borderless_hovered) :style("hovered", style_borderless_hovered)
:style("pressed", style_borderless_hovered) :style("pressed", style_borderless_hovered)
:onclick(function() :onclick(function()
@ -86,7 +98,11 @@ return function(state)
end) end)
imfs.box(0, 0, 0.1, "100%", theme.styles.container.border_color) imfs.box(0, 0, 0.1, "100%", theme.styles.container.border_color)
imfs.button(0, 0, "1x", "100%", "Texture Packs") imfs.button(0, 0, "1x", "100%", "Texture Packs")
:style(show_type == "texturepacks" and style_borderless_alt or style_borderless) :style(
show_type == "texturepacks"
and style_borderless_alt
or style_borderless
)
:style("hovered", style_borderless_hovered) :style("hovered", style_borderless_hovered)
:style("pressed", style_borderless_hovered) :style("pressed", style_borderless_hovered)
:onclick(function() :onclick(function()
@ -157,75 +173,83 @@ return function(state)
return imfs.end_() return imfs.end_()
end end
local content = state.content_shown() if state.detail() then
local num_per_page = math.floor((size.x + 0.5) / 5.5) * math.floor((size.y - 1.05) / 3.5) imfs.button(1, 2, 3, 0.75, "Back")
local excess_x = (size.x + 0.5) % 5.5 :onclick(function()
local excess_y = (size.y - 1.05) % 3.5 state.detail(false)
imfs.scroll_container(0, 1.05, "100%", "100% - 1.55", "horizontal", 0, "") end)
:scrollbar(function() else
imfs.scrollbar(0, size.y - 0.5, size.x, 0.5, "horizontal", state.page()) local content = state.content_shown()
:options({ local num_per_page =
min = 1, math.floor((size.x + 0.5) / 5.5) * math.floor((size.y - 1.05) / 3.5)
max = math.ceil(state.num_content / num_per_page), local excess_x = (size.x + 0.5) % 5.5
smallstep = 1 local excess_y = (size.y - 1.05) % 3.5
}) imfs.scroll_container(0, 1.05, "100%", "100% - 1.55", "horizontal", 0, "")
:onchange(function(action, value) :scrollbar(function()
if action == "CHG" then imfs.scrollbar(0, size.y - 0.5, size.x, 0.5, "horizontal", state.page())
state.page(value) :options({
end min = 1,
end) max = math.ceil(state.num_content / num_per_page),
end) smallstep = 1
})
local x = 0 :onchange(function(action, value)
local y = 0 if action == "CHG" then
local start = (state.page() - 1) * num_per_page state.page(value)
local i = start end
local idx = start end)
while idx < start + num_per_page do end)
i = i + 1
local pkg = content[i]
if not pkg then break end
if not package.is_installed(pkg.type, pkg.name) then local x = 0
idx = idx + 1 local y = 0
local start = (state.page() - 1) * num_per_page
local i = start
local idx = start
while idx < start + num_per_page do
i = i + 1
local pkg = content[i]
if not pkg then break end
imfs.group(x + excess_x / 2, y + excess_y / 2, 5, 3) if not package.is_installed(pkg.type, pkg.name) then
imfs.image(0, 0, "100%", "100%", package.screenshot(pkg)) idx = idx + 1
imfs.box(0, 0, "100%", "100%", "#000a")
imfs.hypertext(0.1, 0.1, "100% - 0.2", "100% - 0.2", string.format( imfs.group(x + excess_x / 2, y + excess_y / 2, 5, 3)
[[<global color=#fff><style size=24><b>%s</b></style> imfs.image(0, 0, "100%", "100%", package.screenshot(pkg))
<b> by %s</b> imfs.box(0, 0, "100%", "100%", "#000a")
%s]], imfs.hypertext(0.1, 0.1, "100% - 0.2", "100% - 0.2", string.format(
core.hypertext_escape(pkg.title or pkg.name), [[<global color=#fff><style size=24><b>%s</b></style>
pkg.author, <b> by %s</b>
pkg.short_description %s]],
)) core.hypertext_escape(pkg.title or pkg.name),
:style(".scrollbar", { pkg.author,
size = "0" pkg.short_description
}) ))
imfs.button(0, 0, "100%", "100%") :style(".scrollbar", {
:styles(styles_no_background) size = "0"
:style("hovered", { })
bgimg = "[fill:1x1:0,0:#fff", imfs.button(0, 0, "100%", "100%")
bgcolor = "#0004" :styles(styles_no_background)
}) :style("hovered", {
:style("pressed", { bgimg = "[fill:1x1:0,0:#fff",
bgimg = "[fill:1x1:0,0:#fff", bgcolor = "#0004"
bgcolor = "#0004" })
}) :style("pressed", {
:onclick(function() bgimg = "[fill:1x1:0,0:#fff",
bgcolor = "#0004"
end) })
imfs.group_end() :onclick(function()
x = x + 5.5 state.detail(pkg)
if x + 5 > size.x then end)
x = 0 imfs.group_end()
y = y + 3.5 x = x + 5.5
if x + 5 > size.x then
x = 0
y = y + 3.5
end
end end
end end
end
imfs.scroll_container_end()
imfs.scroll_container_end() end
return imfs.end_() return imfs.end_()
end end

View file

@ -127,6 +127,17 @@ local function refresh_server_list(state)
nil, nil,
function(result) function(result)
local list = table.copy(state.favorite_servers) local list = table.copy(state.favorite_servers)
local num_servers = #result
local i = 0
while i < num_servers do
i = i + 1
-- Drop incompatible servers. You can't join them,
-- so showing them is rather pointless.
if (result[i].proto_max or version.proto_min) < version.proto_min then
table.remove(result, i)
num_servers = num_servers - 1
end
end
table.insert_all(list, result) table.insert_all(list, result)
state.servers = list state.servers = list
if state.search ~= "" then if state.search ~= "" then
@ -280,109 +291,99 @@ return function(state)
imfs.scope("servers") imfs.scope("servers")
local height = size.y * 0.8 local height = size.y * 0.8
local i = 0 local i = 0
local idx = state.scroll_pos() for idx = state.scroll_pos(), state.scroll_pos() + height / 0.5 do
while true do
-- We must use a while loop here because we skip incompatible servers,
-- and we have no way of knowing how many there will be until we iterate.
if i > math.ceil(height / 0.5) then break end
local x = list[idx] local x = list[idx]
-- End early if we've run out of list. -- End early if we've run out of list.
if not x then break end if not x then break end
-- Skip incompatible servers. You can't join them, local ping_lvl = 0
-- so showing them is rather pointless. local lag = (x.lag or 0) * 1000 + (x.ping or 0) * 250
if (x.proto_max or version.proto_min) >= version.proto_min then if lag <= 125 then
local ping_lvl = 0 ping_lvl = 4
local lag = (x.lag or 0) * 1000 + (x.ping or 0) * 250 elseif lag <= 175 then
if lag <= 125 then ping_lvl = 3
ping_lvl = 4 elseif lag <= 250 then
elseif lag <= 175 then ping_lvl = 2
ping_lvl = 3 elseif lag <= 400 then
elseif lag <= 250 then ping_lvl = 1
ping_lvl = 2 end
elseif lag <= 400 then
ping_lvl = 1 local name = x.name and x.name:trim() or ""
if name == "" then
name = core.colorize(theme.styles.text_color_muted, x.address..":"..x.port)
end
imfs.button(0, i * 0.5, "100%", 0.5)
:style({
border = false,
bgimg = "[fill:1x1:0,0:#fff",
bgimg_middle = 0,
bgcolor = i % 2 == 1 and theme.styles.container.bg_color or theme.styles.container.bg_color_alt,
})
:style("hovered", {
border = false,
bgimg = "[fill:1x1:0,0:#fff",
bgimg_middle = 0,
})
:style("pressed", {
border = false,
bgimg = "[fill:1x1:0,0:#fff",
bgimg_middle = 0,
})
:onclick(function()
state.detail(x)
end)
imfs.row(0.1, i * 0.5, "100% - 0.65", 0.5)
if x.favorite then
imfs.image(0, 0, 0.5, 0.5, theme.icon("menu_servers_favorite"))
:tooltip("Favorite server", enable_tooltips)
end end
local name = x.name and x.name:trim() or "" local name = x.name and x.name:trim() or ""
if name == "" then if name == "" then
name = core.colorize("#888", x.address..":"..x.port) name = core.colorize("#888", x.address..":"..x.port)
end end
-- We use an arealabel to ensure that the name will be clipped
-- if it is inordinately long, rather than overflowing onto
-- other thigs.
imfs.arealabel(0, 0.125, "1x", 0.375, name)
imfs.button(0, i * 0.5, "100%", 0.5) if x.pvp == false then
:style({ imfs.image(0, 0, 0.5, 0.5, theme.icon("menu_servers_peaceful"))
border = false, :tooltip("Peaceful", enable_tooltips)
bgimg = "[fill:1x1:0,0:#fff", end
bgimg_middle = 0,
bgcolor = i % 2 == 1 and theme.styles.container.bg_color or theme.styles.container.bg_color_alt,
})
:style("hovered", {
border = false,
bgimg = "[fill:1x1:0,0:#fff",
bgimg_middle = 0,
})
:style("pressed", {
border = false,
bgimg = "[fill:1x1:0,0:#fff",
bgimg_middle = 0,
})
:onclick(function()
state.detail(x)
end)
imfs.row(0.1, i * 0.5, "100% - 0.65", 0.5)
if x.favorite then
imfs.image(0, 0, 0.5, 0.5, theme.icon("menu_servers_favorite"))
:tooltip("Favorite server", enable_tooltips)
end
local name = x.name and x.name:trim() or "" if x.creative then
if name == "" then imfs.image(0, 0, 0.5, 0.5, theme.icon("menu_servers_creative"))
name = core.colorize("#888", x.address..":"..x.port) :tooltip("Creative", enable_tooltips)
end end
-- We use an arealabel to ensure that the name will be clipped
-- if it is inordinately long, rather than overflowing onto if x.clients then
-- other thigs. local clients = x.clients..(
imfs.arealabel(0, 0.125, "1x", 0.375, name) x.clients_max and "/"..x.clients_max or ""
)
if x.pvp == false then local color = "#aaa"
imfs.image(0, 0, 0.5, 0.5, theme.icon("menu_servers_peaceful")) if x.clients > 0 and x.clients_max then
:tooltip("Peaceful", enable_tooltips) local percent = x.clients /x.clients_max
end if percent < 0.75 then
color = "#638b67"
if x.creative then elseif percent < 1 then
imfs.image(0, 0, 0.5, 0.5, theme.icon("menu_servers_creative")) color = "#a69174"
:tooltip("Creative", enable_tooltips) else
end color = "#9d5b5b"
if x.clients then
local clients = x.clients..(
x.clients_max and "/"..x.clients_max or ""
)
local color = "#aaa"
if x.clients > 0 and x.clients_max then
local percent = x.clients /x.clients_max
if percent < 0.75 then
color = "#638b67"
elseif percent < 1 then
color = "#a69174"
else
color = "#9d5b5b"
end
end end
imfs.hypertext(0, 0, 1, 0.5, "<global valign=middle halign=center color="..color..">"..core.hypertext_escape(clients))
end
if x.ping or x.lag then
imfs.image(0, 0, 0.5, 0.5, theme.icon("menu_servers_icon_ping_"..ping_lvl))
:tooltip("Ping: "..math.floor(lag), enable_tooltips)
end end
imfs.hypertext(0, 0, 1, 0.5, "<global valign=middle halign=center color="..color..">"..core.hypertext_escape(clients))
end
imfs.row_end() if x.ping or x.lag then
imfs.image(0, 0, 0.5, 0.5, theme.icon("menu_servers_icon_ping_"..ping_lvl))
i = i + 1 :tooltip("Ping: "..math.floor(lag), enable_tooltips)
end end
idx = idx + 1
imfs.row_end()
i = i + 1
end end
imfs.scope_end() imfs.scope_end()
@ -467,7 +468,7 @@ return function(state)
end end
if joining == true then if joining == true then
imfs.hypertext(0, 0, "100%", 0.75, "<global valign=middle halign=center color=#aaa>Connecting to server...") imfs.hypertext(0, 0, "100%", 0.75, "<global valign=middle halign=center color=#aaa>Joining server...")
imfs.group(0, 0, "100%", 1.05) imfs.group(0, 0, "100%", 1.05)
imfs.image(0, 0.3, "100%", "100% - 0.3", theme.get_background_image("field")) imfs.image(0, 0.3, "100%", "100% - 0.3", theme.get_background_image("field"))
imfs.label(0.2, 0.125, "Address") imfs.label(0.2, 0.125, "Address")
@ -489,7 +490,7 @@ return function(state)
end) end)
imfs.group_end() imfs.group_end()
else else
imfs.hypertext(0, 0, "100%", 0.75, "<global valign=middle halign=center color=#aaa>Connecting to <b>"..core.hypertext_escape(joining.name or joining.address..":"..joining.port).."</b>...") imfs.hypertext(0, 0, "100%", 0.75, "<global valign=middle halign=center color=#aaa>Joining <b>"..core.hypertext_escape(joining.name or joining.address..":"..joining.port).."</b>...")
end end
theme.field(0, 0, "100%", 0.75, "Username", state.username) theme.field(0, 0, "100%", 0.75, "Username", state.username)

View file

@ -42,14 +42,24 @@ local function search_settings(state, search)
return state.settings, state.categories return state.settings, state.categories
end end
local words = search:lower():trim():split(" ")
local out = {} local out = {}
for category, settings in pairs(state.settings) do for category, settings in pairs(state.settings) do
for i = 1, #settings do for i = 1, #settings do
if settings[i].name:find(search, 1, true) then for j = 1, #words do
if not out[category] then local passed = true
out[category] = {settings[i]} if not (settings[i].readable_name:lower():find(words[j], 1, true)
else or settings[i].name:find(words[j], 1, true)) then
table.insert(out[category], settings[i]) passed = false
end
if passed then
if not out[category] then
out[category] = {settings[i]}
else
table.insert(out[category], settings[i])
end
end end
end end
end end