diff --git a/w-engine/plugin.toml b/w-engine/plugin.toml index 13793290..b47c3586 100644 --- a/w-engine/plugin.toml +++ b/w-engine/plugin.toml @@ -1,6 +1,6 @@ id = "tadomika_ari/w-engine" name = "W Engine" -version = "1.1.1" +version = "1.2.0" # Tile and control callbacks are Luau closures rather than named globals, which # the host resolves from plugin API 9 onwards. plugin_api = 9 diff --git a/w-engine/start.luau b/w-engine/start.luau index 9d135f06..32eccac2 100644 --- a/w-engine/start.luau +++ b/w-engine/start.luau @@ -27,6 +27,7 @@ local data = { saved_wallpaper = {}, -- output -> the Noctalia wallpaper from before we took over options = {}, -- id -> { engine = {...}, properties = {...} }, see buildLaunchArgs defaults = {}, -- { engine = {...} } applied to every wallpaper, see optionsFor + favorites = {}, -- favorites wallpapers list } -- linux-wallpaperengine switches that take no value. Stored as booleans; the @@ -103,6 +104,7 @@ local function loadData() data.saved_wallpaper = asTable(decoded.saved_wallpaper) data.options = asTable(decoded.options) data.defaults = asTable(decoded.defaults) + data.favorites = asTable(decoded.favorites) end local function saveData() @@ -233,7 +235,7 @@ local function resolveColorSource(id, callback) -- names contain spaces and non-ASCII, so paths are quoted. local cmd = "ffmpeg -y -loglevel error -ss 1 -i " .. shellQuote(dir .. file) - .. " -frames:v 1 -vf scale=960:-2 " + .. " -frames:v 1 -vf scale=960:-2" .. shellQuote(frame) noctalia.runAsync(cmd, function(result) -- A wallpaper shorter than the seek offset produces no output and no error, diff --git a/w-engine/thumbnail.webp b/w-engine/thumbnail.webp index cea1394f..13c06209 100644 Binary files a/w-engine/thumbnail.webp and b/w-engine/thumbnail.webp differ diff --git a/w-engine/w-engine-panel.luau b/w-engine/w-engine-panel.luau index 265994d0..529b6c4b 100644 --- a/w-engine/w-engine-panel.luau +++ b/w-engine/w-engine-panel.luau @@ -1170,12 +1170,83 @@ local function tilePreview(item, isSelected, isCurrent) }) end +-- Favorite list + +local FavoriteList = {} + +local function getDataPath() + return noctalia.pluginDataDir() .. "/data.json" +end + +local function loadData() + local raw = noctalia.readFile(getDataPath()) + if not raw then + return {} + end + local ok, decoded = pcall(function() + return noctalia.json.decode(raw) + end) + if not ok or type(decoded) ~= "table" then + return {} + end + return decoded +end + +local function saveData(data) + return noctalia.writeFile(getDataPath(), noctalia.json.encode(data)) +end + +local function loadFavoriteList() + local data = loadData() + FavoriteList = data.favorites or {} +end + +local function persistFavoriteList() + local data = loadData() -- récupère les autres clés existantes (ex: settingsPath) + data.favorites = FavoriteList + saveData(data) +end + +local function addFavoriteList(item) + noctalia.notify("Add Favorite") + table.insert(FavoriteList, item) + persistFavoriteList() +end + +local function removeFavoriteList(item, nb) + noctalia.notify("Remove Favorite") + table.remove(FavoriteList, nb) + persistFavoriteList() +end + +local function checkInFavoriteList(item) + local nb = 1 + for _, entryName in ipairs(FavoriteList) do + if entryName.name == item.name then + removeFavoriteList(item, nb) + return + end + nb = nb + 1 + end + addFavoriteList(item) +end + local function tileCaption(item, index, isSelected) -- Numbered to show rotation order. local caption = isSelected and (tostring(index) .. ". " .. item.name) or item.name -- A fixed label width keeps every tile the same size; long titles truncate. return ui.row({ gap = 2, align = "center" }, { + ui.button({ + glyph = "star", + variant = "ghost", + controlSize = "sm", + tooltip = "Add at your favorite wallpaper", + onClick = function() + checkInFavoriteList(item) + render() + end + }), ui.label({ text = caption, fontSize = 11, @@ -1239,15 +1310,45 @@ render = function() if #catalog == 0 then table.insert(body, ui.label({ text = tr("panel.no_wallpapers"), fontSize = 12, color = "on_surface_variant" })) end - -- paddingRight keeps the last column clear of the scrollbar. - table.insert( - body, - ui.scroll({ key = "grid-scroll", gap = 12, paddingRight = 8, align = "stretch", flexGrow = 1 }, rows) - ) + + local favoriteRows = {} + local favoriteRow = {} + for index, item in ipairs(FavoriteList) do + table.insert(favoriteRow, tile(item, state)) + if #favoriteRow == columns or index == #FavoriteList then + table.insert(favoriteRows, ui.row({ gap = 12, align = "start" }, favoriteRow)) + favoriteRow = {} + end + end + + -- Single scroll for all list (Wallpaper and Favorite Wallpaper) + local scrollContent = {} + table.insert(scrollContent, ui.label({ text = "Favorites", fontSize = 13, fontWeight = "bold", color = "on_surface" })) + if #FavoriteList == 0 then + table.insert(scrollContent, ui.label({ text = "No favorite", fontSize = 12, color = "on_surface_variant" })) + else + for _, r in ipairs(favoriteRows) do + table.insert(scrollContent, r) + end + end + + table.insert(scrollContent, + ui.separator({ spacing = 12 } + )) + + table.insert(scrollContent, + ui.label({ text = "All wallpapers", fontSize = 13, fontWeight = "bold", color = "on_surface" } + )) + + for _, r in ipairs(rows) do + table.insert(scrollContent, r) + end + + table.insert(body, + ui.scroll({ key = "grid-scroll", gap = 12, paddingRight = 8, align = "stretch", flexGrow = 1 }, scrollContent)) panel.render(ui.column({ flexGrow = 1, gap = 12 }, body)) end - -- ── Handlers ───────────────────────────────────────────────────────────────── -- Adopts the service's view of this output. Selection, interval and order are @@ -1291,6 +1392,7 @@ end function onOpen(_context) isOpen = true + loadFavoriteList() outputName = noctalia.focusedOutputName() or outputName if not outputName then local outputs = noctalia.outputs()