mirror of
https://github.com/noctalia-dev/noctalia-shell.git
synced 2026-05-11 17:08:27 +08:00
feat(launcher): add wallpaper provider
This commit is contained in:
@@ -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',
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
};
|
||||
Reference in New Issue
Block a user