feat(launcher): add wallpaper provider

This commit is contained in:
Ly-sec
2026-04-27 19:15:02 +02:00
parent 4fa573c7c1
commit 3eb445fd4c
4 changed files with 162 additions and 0 deletions
+1
View File
@@ -339,6 +339,7 @@ _noctalia_sources = files(
'src/launcher/app_provider.cpp',
'src/launcher/emoji_provider.cpp',
'src/launcher/math_provider.cpp',
'src/launcher/wallpaper_provider.cpp',
'src/launcher/usage_tracker.cpp',
'src/main.cpp',
'src/net/http_client.cpp',
+2
View File
@@ -10,6 +10,7 @@
#include "launcher/app_provider.h"
#include "launcher/emoji_provider.h"
#include "launcher/math_provider.h"
#include "launcher/wallpaper_provider.h"
#include "notification/notifications.h"
#include "render/animation/motion_service.h"
#include "shell/clipboard/clipboard_panel.h"
@@ -748,6 +749,7 @@ void Application::initUi() {
{
auto launcherPanel = std::make_unique<LauncherPanel>(&m_configService, &m_asyncTextureCache);
launcherPanel->addProvider(std::make_unique<AppProvider>(&m_wayland));
launcherPanel->addProvider(std::make_unique<WallpaperProvider>(&m_configService, &m_wayland));
launcherPanel->addProvider(std::make_unique<MathProvider>(&m_clipboardService));
launcherPanel->addProvider(std::make_unique<EmojiProvider>(&m_clipboardService));
m_panelManager.registerPanel("launcher", std::move(launcherPanel));
+136
View File
@@ -0,0 +1,136 @@
#include "launcher/wallpaper_provider.h"
#include "config/config_service.h"
#include "util/fuzzy_match.h"
#include "util/string_utils.h"
#include "wayland/wayland_connection.h"
#include <algorithm>
#include <cctype>
#include <filesystem>
#include <system_error>
#include <vector>
namespace {
constexpr std::size_t kMaxResults = 50;
struct WallpaperCandidate {
std::string name;
std::string path;
std::string searchable;
};
bool hasImageExtension(const std::filesystem::path& path) {
auto ext = path.extension().string();
std::transform(ext.begin(), ext.end(), ext.begin(),
[](unsigned char c) { return static_cast<char>(std::tolower(c)); });
return ext == ".jpg" || ext == ".jpeg" || ext == ".png" || ext == ".webp" || ext == ".bmp" || ext == ".gif";
}
std::filesystem::path wallpaperDirectory(const WallpaperConfig& wallpaper, ThemeMode mode) {
if (mode == ThemeMode::Light && !wallpaper.directoryLight.empty()) {
return wallpaper.directoryLight;
}
if (mode == ThemeMode::Dark && !wallpaper.directoryDark.empty()) {
return wallpaper.directoryDark;
}
return wallpaper.directory;
}
std::vector<WallpaperCandidate> collectWallpapers(const std::filesystem::path& directory) {
std::vector<WallpaperCandidate> candidates;
if (directory.empty()) {
return candidates;
}
std::error_code ec;
if (!std::filesystem::exists(directory, ec) || !std::filesystem::is_directory(directory, ec)) {
return candidates;
}
for (auto it = std::filesystem::recursive_directory_iterator(
directory, std::filesystem::directory_options::skip_permission_denied, ec);
!ec && it != std::filesystem::end(it); it.increment(ec)) {
if (ec) {
break;
}
std::error_code typeEc;
if (!it->is_regular_file(typeEc) || typeEc || !hasImageExtension(it->path())) {
continue;
}
WallpaperCandidate candidate;
candidate.name = it->path().filename().string();
candidate.path = it->path().string();
candidate.searchable = StringUtils::toLower(candidate.name + " " + it->path().parent_path().filename().string());
candidates.push_back(std::move(candidate));
}
std::sort(candidates.begin(), candidates.end(),
[](const auto& a, const auto& b) { return StringUtils::toLower(a.name) < StringUtils::toLower(b.name); });
return candidates;
}
} // namespace
WallpaperProvider::WallpaperProvider(ConfigService* config, WaylandConnection* wayland)
: m_config(config), m_wayland(wayland) {}
std::vector<LauncherResult> WallpaperProvider::query(std::string_view text) const {
if (m_config == nullptr) {
return {};
}
const std::string query = StringUtils::toLower(StringUtils::trim(text));
auto candidates = collectWallpapers(wallpaperDirectory(m_config->config().wallpaper, m_config->config().theme.mode));
if (candidates.empty()) {
return {};
}
std::vector<std::pair<int, WallpaperCandidate>> scored;
scored.reserve(candidates.size());
for (auto& candidate : candidates) {
const int score = query.empty() ? 1 : FuzzyMatch::score(query, candidate.searchable);
if (score > 0) {
scored.emplace_back(score, std::move(candidate));
}
}
const auto limit = std::min(scored.size(), kMaxResults);
std::partial_sort(scored.begin(), scored.begin() + static_cast<std::ptrdiff_t>(limit), scored.end(),
[](const auto& a, const auto& b) { return a.first > b.first; });
std::vector<LauncherResult> results;
results.reserve(limit);
for (std::size_t i = 0; i < limit; ++i) {
const auto& [score, candidate] = scored[i];
LauncherResult result;
result.id = candidate.path;
result.title = candidate.name;
result.subtitle = candidate.path;
result.glyphName = "wallpaper-selector";
result.iconPath = candidate.path;
result.score = score;
results.push_back(std::move(result));
}
return results;
}
bool WallpaperProvider::activate(const LauncherResult& result) {
if (m_config == nullptr || result.id.empty()) {
return false;
}
ConfigService::WallpaperBatch batch(*m_config);
if (m_wayland != nullptr) {
for (const auto& out : m_wayland->outputs()) {
if (!out.connectorName.empty()) {
m_config->setWallpaperPath(out.connectorName, result.id);
}
}
}
m_config->setWallpaperPath(std::nullopt, result.id);
return true;
}
+23
View File
@@ -0,0 +1,23 @@
#pragma once
#include "launcher/launcher_provider.h"
class ConfigService;
class WaylandConnection;
class WallpaperProvider : public LauncherProvider {
public:
WallpaperProvider(ConfigService* config, WaylandConnection* wayland);
[[nodiscard]] std::string_view prefix() const override { return "/wall"; }
[[nodiscard]] std::string_view name() const override { return "Wallpaper"; }
[[nodiscard]] bool trackUsage() const override { return true; }
[[nodiscard]] std::vector<LauncherResult> query(std::string_view text) const override;
bool activate(const LauncherResult& result) override;
private:
ConfigService* m_config = nullptr;
WaylandConnection* m_wayland = nullptr;
};