refactor: all your string_utils belongs to me

This commit is contained in:
Lemmy
2026-05-10 23:22:34 -04:00
parent c13281e2bb
commit b36f2a836e
31 changed files with 327 additions and 532 deletions
@@ -838,18 +838,6 @@ std::vector<std::string_view> HyprlandWorkspaceBackend::parseEventArgs(std::stri
return args;
}
std::string HyprlandWorkspaceBackend::quoteCommandArg(const std::string& value) {
std::string escaped = "\"";
for (const char c : value) {
if (c == '\\' || c == '"') {
escaped.push_back('\\');
}
escaped.push_back(c);
}
escaped.push_back('"');
return escaped;
}
Workspace HyprlandWorkspaceBackend::toWorkspace(const WorkspaceState& state) {
const std::uint32_t coord =
state.id >= 0 ? static_cast<std::uint32_t>(state.id - 1) : static_cast<std::uint32_t>(state.ordinal);
@@ -84,7 +84,6 @@ private:
[[nodiscard]] static std::optional<std::uint64_t> parseHexAddress(std::string_view value);
[[nodiscard]] static std::optional<int> parseInt(std::string_view value);
[[nodiscard]] static std::vector<std::string_view> parseEventArgs(std::string_view data, std::size_t count);
[[nodiscard]] static std::string quoteCommandArg(const std::string& value);
[[nodiscard]] static Workspace toWorkspace(const WorkspaceState& state);
OutputNameResolver m_outputNameResolver;
@@ -2,6 +2,7 @@
#include "compositors/sway/sway_runtime.h"
#include "core/log.h"
#include "util/string_utils.h"
#include <arpa/inet.h>
#include <cerrno>
@@ -203,12 +204,6 @@ namespace {
}
}
std::string toLowerCopy(std::string value) {
std::transform(value.begin(), value.end(), value.begin(),
[](unsigned char ch) { return static_cast<char>(std::tolower(ch)); });
return value;
}
std::string assignmentLookupKey(std::string_view workspaceKey, std::string_view appId, std::string_view title) {
std::string key;
key.reserve(workspaceKey.size() + appId.size() + title.size() + 2);
@@ -272,7 +267,7 @@ void SwayWorkspaceBackend::activate(const std::string& id) {
return;
}
sendMessage(kIpcRunCommand, "workspace " + quoteCommandArg(id));
sendMessage(kIpcRunCommand, "workspace " + StringUtils::quoteDouble(id));
}
void SwayWorkspaceBackend::activateForOutput(wl_output* /*output*/, const std::string& id) { activate(id); }
@@ -352,7 +347,7 @@ SwayWorkspaceBackend::assignTaskbarWindows(const std::vector<TaskbarWindowCandid
std::unordered_map<std::string, std::vector<const WorkspaceWindow*>> windowsByLookupKey;
windowsByLookupKey.reserve(outputWindows.size());
for (const auto& window : outputWindows) {
const std::string appIdLower = toLowerCopy(window.appId);
const std::string appIdLower = StringUtils::toLower(window.appId);
windowsByLookupKey[assignmentLookupKey(window.workspaceKey, appIdLower, window.title)].push_back(&window);
}
@@ -376,7 +371,8 @@ SwayWorkspaceBackend::assignTaskbarWindows(const std::vector<TaskbarWindowCandid
}
for (const auto& candidateAppId : window.appIds) {
const std::string lookupKey = assignmentLookupKey(workspaceKey, toLowerCopy(candidateAppId), window.title);
const std::string lookupKey =
assignmentLookupKey(workspaceKey, StringUtils::toLower(candidateAppId), window.title);
auto it = windowsByLookupKey.find(lookupKey);
if (it == windowsByLookupKey.end()) {
continue;
@@ -667,15 +663,3 @@ Workspace SwayWorkspaceBackend::toWorkspace(const SwayWorkspace& workspace) {
.occupied = workspace.occupied,
};
}
std::string SwayWorkspaceBackend::quoteCommandArg(const std::string& value) {
std::string escaped = "\"";
for (const char c : value) {
if (c == '\\' || c == '"') {
escaped.push_back('\\');
}
escaped.push_back(c);
}
escaped.push_back('"');
return escaped;
}
@@ -64,7 +64,6 @@ private:
void requestTree();
void refreshFromWorkspaceEvent();
[[nodiscard]] static Workspace toWorkspace(const SwayWorkspace& workspace);
[[nodiscard]] static std::string quoteCommandArg(const std::string& value);
OutputNameResolver m_outputNameResolver;
compositors::sway::SwayRuntime& m_runtime;
+3 -15
View File
@@ -1,6 +1,7 @@
#include "config/cli.h"
#include "core/toml.h"
#include "util/string_utils.h"
#include <cstdio>
#include <cstring>
@@ -39,19 +40,6 @@ namespace noctalia::config {
bool force = false;
};
std::string shellQuote(std::string_view raw) {
std::string out = "'";
for (const char c : raw) {
if (c == '\'') {
out += "'\\''";
} else {
out.push_back(c);
}
}
out.push_back('\'');
return out;
}
bool writeTextFile(const std::filesystem::path& path, std::string_view content, std::string& error) {
std::error_code ec;
std::filesystem::create_directories(path.parent_path(), ec);
@@ -257,8 +245,8 @@ namespace noctalia::config {
std::printf("Config home: %s\n", configHome.string().c_str());
std::printf("State home: %s\n\n", stateHome.string().c_str());
std::printf("Run with:\n");
std::printf(" XDG_CONFIG_HOME=%s XDG_STATE_HOME=%s %s\n", shellQuote(configHome.string()).c_str(),
shellQuote(stateHome.string()).c_str(), shellQuote(argv0).c_str());
std::printf(" XDG_CONFIG_HOME=%s XDG_STATE_HOME=%s %s\n", StringUtils::shellQuote(configHome.string()).c_str(),
StringUtils::shellQuote(stateHome.string()).c_str(), StringUtils::shellQuote(argv0).c_str());
return 0;
}
+3 -24
View File
@@ -1,24 +1,12 @@
#include "core/files/directory_scanner.h"
#include "util/string_utils.h"
#include <algorithm>
#include <array>
#include <cctype>
#include <numeric>
#include <system_error>
namespace {
std::string lowerText(std::string_view text) {
std::string out;
out.reserve(text.size());
for (char ch : text) {
out.push_back(static_cast<char>(std::tolower(static_cast<unsigned char>(ch))));
}
return out;
}
} // namespace
std::vector<FileEntry> DirectoryScanner::scan(const std::filesystem::path& dir,
const std::vector<std::string>& extensions, bool showHiddenFiles,
FileDialogSortField sortField, FileDialogSortOrder sortOrder) const {
@@ -92,7 +80,7 @@ std::vector<FileEntry> DirectoryScanner::scan(const std::filesystem::path& dir,
std::vector<std::string> lowerNames;
lowerNames.reserve(entries.size());
for (const auto& entry : entries) {
lowerNames.push_back(lowerText(entry.name));
lowerNames.push_back(StringUtils::toLower(entry.name));
}
std::vector<std::size_t> indices(entries.size());
@@ -178,12 +166,3 @@ std::string DirectoryScanner::normalizeExtension(std::string_view extension) {
}
return out;
}
std::string DirectoryScanner::lower(std::string_view text) {
std::string out;
out.reserve(text.size());
for (char ch : text) {
out.push_back(static_cast<char>(std::tolower(static_cast<unsigned char>(ch))));
}
return out;
}
-1
View File
@@ -38,5 +38,4 @@ private:
const std::vector<std::string>& extensions);
[[nodiscard]] static bool isHiddenName(std::string_view name);
[[nodiscard]] static std::string normalizeExtension(std::string_view extension);
[[nodiscard]] static std::string lower(std::string_view text);
};
-13
View File
@@ -207,19 +207,6 @@ namespace {
return out;
}
[[maybe_unused]] std::string joinStrings(const std::vector<std::string>& values) {
std::string out;
bool first = true;
for (const auto& value : values) {
if (!first) {
out += ", ";
}
first = false;
out += value;
}
return out;
}
bool hasStrongNowPlayingMetadata(const MprisPlayerInfo& info) {
// Track IDs/source URLs can exist during transient "loading" states where the
// user-visible metadata is still placeholder-only (e.g. app identity + logo).
@@ -132,11 +132,6 @@ namespace {
return std::string{s.substr(0, len)};
}
bool isBlankText(std::string_view text) {
return text.empty() ||
std::all_of(text.begin(), text.end(), [](unsigned char ch) { return std::isspace(ch) != 0; });
}
std::vector<std::string> sanitize_actions(const std::vector<std::string>& actions) {
std::vector<std::string> sanitized;
sanitized.reserve(actions.size() - (actions.size() % 2));
@@ -149,7 +144,7 @@ namespace {
continue;
}
if (isBlankText(label)) {
if (StringUtils::isBlank(label)) {
label = i18n::tr("notifications.actions.fallback");
}
+1 -15
View File
@@ -40,20 +40,6 @@ namespace {
bool looks_like_dbus_name(std::string_view value) { return !value.empty() && value != "__path_only__"; }
std::string trim(std::string value) {
while (!value.empty() && std::isspace(static_cast<unsigned char>(value.back())) != 0) {
value.pop_back();
}
std::size_t first = 0;
while (first < value.size() && std::isspace(static_cast<unsigned char>(value[first])) != 0) {
++first;
}
if (first > 0) {
value.erase(0, first);
}
return value;
}
std::string processNameForPid(std::uint32_t pid) {
if (pid == 0) {
return {};
@@ -69,7 +55,7 @@ namespace {
std::ifstream comm(procDir / "comm");
std::string name;
if (std::getline(comm, name)) {
return trim(std::move(name));
return StringUtils::trim(name);
}
return {};
}
+3 -10
View File
@@ -1,23 +1,16 @@
#pragma once
#include "util/string_utils.h"
#include <cmath>
#include <optional>
#include <sstream>
#include <string>
#include <string_view>
#include <vector>
namespace noctalia::ipc {
inline std::vector<std::string> splitWords(std::string_view text) {
std::vector<std::string> words;
std::istringstream stream{std::string(text)};
std::string word;
while (stream >> word) {
words.push_back(std::move(word));
}
return words;
}
inline std::vector<std::string> splitWords(std::string_view text) { return StringUtils::splitWhitespace(text); }
inline std::optional<float> parseNormalizedOrPercent(std::string_view token, float maxPercent = 100.0f) {
std::string value(token);
+2 -8
View File
@@ -2,6 +2,7 @@
#include "util/file_utils.h"
#include "util/fuzzy_match.h"
#include "util/string_utils.h"
#include "wayland/wayland_connection.h"
#include <algorithm>
@@ -20,13 +21,6 @@ namespace {
constexpr std::size_t kMaxSearchResults = 50;
constexpr std::string_view kDefaultAppIcon = "application-x-executable";
std::string toLower(std::string_view s) {
std::string result(s);
std::transform(result.begin(), result.end(), result.begin(),
[](unsigned char c) { return static_cast<char>(std::tolower(c)); });
return result;
}
double scoreEntry(std::string_view pattern, const DesktopEntry& entry) {
if (pattern.empty()) {
return 0.0;
@@ -288,7 +282,7 @@ void AppProvider::refreshEntriesIfNeeded() const {
std::vector<LauncherResult> AppProvider::query(std::string_view text) const {
refreshEntriesIfNeeded();
const std::string normalizedText = toLower(text);
const std::string normalizedText = StringUtils::toLower(text);
const std::string_view pattern = normalizedText;
auto buildResult = [&](const DesktopEntry& entry, double s) {
+3 -14
View File
@@ -10,23 +10,12 @@
#include "ui/controls/label.h"
#include "ui/palette.h"
#include "ui/style.h"
#include "util/string_utils.h"
#include <algorithm>
#include <cctype>
#include <cmath>
#include <string_view>
namespace {
std::string toLower(std::string_view value) {
std::string out(value);
std::transform(out.begin(), out.end(), out.begin(),
[](unsigned char c) { return static_cast<char>(std::tolower(c)); });
return out;
}
} // namespace
ActiveWindowWidget::ActiveWindowWidget(CompositorPlatform& platform, float maxWidth, float minWidth, float iconSize,
ActiveWindowTitleScrollMode titleScrollMode)
: m_platform(platform), m_maxWidth(maxWidth), m_minWidth(minWidth), m_iconSize(iconSize),
@@ -212,7 +201,7 @@ std::string ActiveWindowWidget::resolveIconPath(const std::string& appId) {
}
}
const std::string appIdLower = toLower(appId);
const std::string appIdLower = StringUtils::toLower(appId);
if (auto it = m_appIcons.find(appIdLower); it != m_appIcons.end()) {
const auto path = resolveByName(it->second);
if (!path.empty()) {
@@ -240,7 +229,7 @@ void ActiveWindowWidget::buildDesktopIconIndex() {
return;
}
m_appIcons.try_emplace(std::string{key}, icon);
m_appIcons.try_emplace(toLower(key), icon);
m_appIcons.try_emplace(StringUtils::toLower(key), icon);
};
const auto& entries = desktopEntries();
+7 -105
View File
@@ -8,14 +8,15 @@
#include "render/scene/node.h"
#include "render/text/glyph_registry.h"
#include "shell/panel/panel_manager.h"
#include "shell/tray/tray_identifier.h"
#include "ui/controls/flex.h"
#include "ui/controls/glyph.h"
#include "ui/controls/image.h"
#include "ui/palette.h"
#include "ui/style.h"
#include "util/string_utils.h"
#include <algorithm>
#include <cctype>
#include <cmath>
#include <filesystem>
#include <linux/input-event-codes.h>
@@ -28,106 +29,7 @@ namespace {
constexpr Logger kLog("tray");
std::string toLower(std::string_view value) {
std::string out(value);
std::transform(out.begin(), out.end(), out.begin(),
[](unsigned char c) { return static_cast<char>(std::tolower(c)); });
return out;
}
std::vector<std::string> identifierVariants(std::string_view value) {
std::vector<std::string> out;
if (value.empty()) {
return out;
}
auto pushUnique = [&out](std::string candidate) {
if (candidate.empty()) {
return;
}
if (std::ranges::find(out, candidate) == out.end()) {
out.push_back(std::move(candidate));
}
};
std::string base(value);
pushUnique(base);
pushUnique(toLower(base));
if (const auto slash = base.find_last_of('/'); slash != std::string::npos && slash + 1 < base.size()) {
base = base.substr(slash + 1);
pushUnique(base);
pushUnique(toLower(base));
}
std::string dashed = base;
std::replace(dashed.begin(), dashed.end(), '_', '-');
pushUnique(dashed);
pushUnique(toLower(dashed));
std::string underscored = base;
std::replace(underscored.begin(), underscored.end(), '-', '_');
pushUnique(underscored);
pushUnique(toLower(underscored));
auto pushReducedForms = [&pushUnique](std::string candidate) {
if (candidate.empty()) {
return;
}
pushUnique(candidate);
pushUnique(toLower(candidate));
bool changed = true;
while (changed && !candidate.empty()) {
changed = false;
for (const auto& suffix : {"_client", "-client", ".desktop", "_indicator", "-indicator", "_tray", "-tray",
"_status", "-status", "_panel", "-panel"}) {
if (candidate.size() > std::char_traits<char>::length(suffix) && candidate.ends_with(suffix)) {
candidate = candidate.substr(0, candidate.size() - std::char_traits<char>::length(suffix));
pushUnique(candidate);
pushUnique(toLower(candidate));
changed = true;
break;
}
}
if (changed || candidate.empty()) {
continue;
}
const auto separator = candidate.find_last_of("-_");
if (separator != std::string::npos && separator + 1 < candidate.size()) {
const std::string tail = candidate.substr(separator + 1);
const bool numericTail = std::ranges::all_of(tail, [](unsigned char c) { return std::isdigit(c) != 0; });
if (numericTail) {
candidate = candidate.substr(0, separator);
pushUnique(candidate);
pushUnique(toLower(candidate));
changed = true;
continue;
}
}
for (const auto& suffix : {"-linux", "_linux"}) {
if (candidate.size() > std::char_traits<char>::length(suffix) && candidate.ends_with(suffix)) {
candidate = candidate.substr(0, candidate.size() - std::char_traits<char>::length(suffix));
pushUnique(candidate);
pushUnique(toLower(candidate));
changed = true;
break;
}
}
}
};
for (const auto& candidate : std::vector<std::string>{base, dashed, underscored}) {
pushReducedForms(candidate);
}
return out;
}
using tray::identifierVariants;
void addIconAlias(std::unordered_map<std::string, std::string>& index, std::string_view key, std::string_view icon) {
if (key.empty() || icon.empty()) {
@@ -227,7 +129,7 @@ std::string TrayWidget::resolveFromTrayThemePath(std::string_view themePath, std
}
const fs::path path = it->path();
const auto extension = toLower(path.extension().string());
const auto extension = StringUtils::toLower(path.extension().string());
if (extension != ".svg" && extension != ".png") {
continue;
}
@@ -570,7 +472,7 @@ void TrayWidget::rebuild(Renderer& renderer) {
if (const auto it = m_appIcons.find(overlayName); it != m_appIcons.end()) {
return m_iconResolver.resolve(it->second);
}
const std::string lower = toLower(overlayName);
const std::string lower = StringUtils::toLower(overlayName);
if (const auto it = m_appIcons.find(lower); it != m_appIcons.end()) {
return m_iconResolver.resolve(it->second);
}
@@ -803,7 +705,7 @@ std::string TrayWidget::resolveIconPath(const TrayItemInfo& item) {
}
}
const std::string lower = toLower(name);
const std::string lower = StringUtils::toLower(name);
if (const auto it = m_appIcons.find(lower); it != m_appIcons.end()) {
if (const auto mapped = m_iconResolver.resolve(it->second); !mapped.empty()) {
return mapped;
@@ -817,7 +719,7 @@ std::string TrayWidget::resolveIconPath(const TrayItemInfo& item) {
return mapped;
}
}
const std::string tailLower = toLower(tail);
const std::string tailLower = StringUtils::toLower(tail);
if (const auto it = m_appIcons.find(tailLower); it != m_appIcons.end()) {
if (const auto mapped = m_iconResolver.resolve(it->second); !mapped.empty()) {
return mapped;
+4 -27
View File
@@ -23,6 +23,7 @@
#include "ui/controls/virtual_grid_view.h"
#include "ui/palette.h"
#include "ui/style.h"
#include "util/string_utils.h"
#include "wayland/clipboard_service.h"
#include <algorithm>
@@ -47,30 +48,6 @@ namespace {
constexpr auto kFilterDebounceInterval = std::chrono::milliseconds(120);
constexpr Logger kLog("clipboard");
std::string trim(std::string_view text) {
const auto first = text.find_first_not_of(" \t\r\n");
if (first == std::string_view::npos) {
return {};
}
const auto last = text.find_last_not_of(" \t\r\n");
return std::string(text.substr(first, last - first + 1));
}
std::string shellQuote(std::string_view text) {
std::string quoted;
quoted.reserve(text.size() + 2);
quoted.push_back('\'');
for (char ch : text) {
if (ch == '\'') {
quoted += "'\\''";
} else {
quoted.push_back(ch);
}
}
quoted.push_back('\'');
return quoted;
}
void replaceAll(std::string& text, std::string_view needle, std::string_view replacement) {
if (needle.empty()) {
return;
@@ -86,7 +63,7 @@ namespace {
std::string buildImageActionCommand(std::string command, std::string_view imagePath) {
const bool hasPathPlaceholder = command.find("{path}") != std::string::npos;
const bool hasStdinPlaceholder = command.find("{stdin}") != std::string::npos;
const std::string quotedPath = shellQuote(imagePath);
const std::string quotedPath = StringUtils::shellQuote(imagePath);
if (hasPathPlaceholder) {
replaceAll(command, "{path}", quotedPath);
@@ -866,7 +843,7 @@ void ClipboardPanel::updatePreviewActions() {
bool showImageAction = false;
if (m_clipboard != nullptr && m_config != nullptr &&
!trim(m_config->config().shell.clipboardImageActionCommand).empty()) {
!StringUtils::trim(m_config->config().shell.clipboardImageActionCommand).empty()) {
const std::size_t historyIndex = selectedHistoryIndex();
const auto& history = m_clipboard->history();
showImageAction = historyIndex != static_cast<std::size_t>(-1) && historyIndex < history.size() &&
@@ -1113,7 +1090,7 @@ void ClipboardPanel::runImageAction() {
return;
}
const std::string configuredCommand = trim(m_config->config().shell.clipboardImageActionCommand);
const std::string configuredCommand = StringUtils::trim(m_config->config().shell.clipboardImageActionCommand);
if (configuredCommand.empty()) {
return;
}
+8 -30
View File
@@ -23,9 +23,9 @@
#include "ui/controls/scroll_view.h"
#include "ui/controls/slider.h"
#include "ui/palette.h"
#include "util/string_utils.h"
#include <algorithm>
#include <cctype>
#include <cmath>
#include <cstddef>
#include <memory>
@@ -230,29 +230,7 @@ namespace {
return value;
}
std::string trimAsciiWhitespaceCopy(std::string_view s) {
std::size_t i = 0;
std::size_t j = s.size();
while (i < j && std::isspace(static_cast<unsigned char>(s[i])) != 0) {
++i;
}
while (j > i && std::isspace(static_cast<unsigned char>(s[j - 1])) != 0) {
--j;
}
return std::string(s.substr(i, j - i));
}
[[nodiscard]] bool isBlankSearchKey(std::string_view s) {
if (s.empty()) {
return true;
}
for (unsigned char c : s) {
if (std::isspace(c) == 0) {
return false;
}
}
return true;
}
[[nodiscard]] bool isBlankSearchKey(std::string_view s) { return StringUtils::isBlank(s); }
bool isDesktopTokenDelimiter(unsigned char c) { return c == '-' || c == '_' || c == '.' || std::isspace(c) != 0; }
@@ -436,7 +414,7 @@ namespace {
}
const DesktopEntry* findDesktopEntryByTerm(std::string_view term) {
const std::string trimmed = trimAsciiWhitespaceCopy(term);
const std::string trimmed = StringUtils::trim(term);
if (trimmed.empty()) {
return nullptr;
}
@@ -460,7 +438,7 @@ namespace {
DesktopEntryMatch lookupDesktopEntryForProgramStream(const AudioNode& node, std::string_view resolvedBeforeDesktop) {
DesktopEntryMatch out;
const std::string binary = lowerIdentifier(trimAsciiWhitespaceCopy(node.applicationBinary));
const std::string binary = lowerIdentifier(StringUtils::trim(node.applicationBinary));
// Wine/Proton streams report wine64-preloader etc.; matching desktop entries by that binary (or
// the shared Icon=wine) incorrectly picks unrelated apps (e.g. Protontricks) before app/node name.
if (!binary.empty() && !looksLikeRuntimeLauncher(node.applicationBinary)) {
@@ -471,7 +449,7 @@ namespace {
return out;
}
}
const std::string appId = lowerIdentifier(trimAsciiWhitespaceCopy(node.applicationId));
const std::string appId = lowerIdentifier(StringUtils::trim(node.applicationId));
if (!appId.empty()) {
if (const DesktopEntry* entry = findDesktopEntryByTerm(appId)) {
out.entry = entry;
@@ -480,7 +458,7 @@ namespace {
return out;
}
}
const std::string appName = lowerIdentifier(trimAsciiWhitespaceCopy(node.applicationName));
const std::string appName = lowerIdentifier(StringUtils::trim(node.applicationName));
if (!appName.empty() && !isGenericAudioLabel(appName) && !looksLikeRuntimeLauncher(appName)) {
if (const DesktopEntry* entry = findDesktopEntryByTerm(appName)) {
out.entry = entry;
@@ -489,7 +467,7 @@ namespace {
return out;
}
}
const std::string resolved = lowerIdentifier(trimAsciiWhitespaceCopy(resolvedBeforeDesktop));
const std::string resolved = lowerIdentifier(StringUtils::trim(resolvedBeforeDesktop));
if (!resolved.empty() && !isGenericAudioLabel(resolved) && !looksLikeRuntimeLauncher(resolved)) {
if (const DesktopEntry* entry = findDesktopEntryByTerm(resolved)) {
out.entry = entry;
@@ -498,7 +476,7 @@ namespace {
return out;
}
}
const std::string nodeName = lowerIdentifier(trimAsciiWhitespaceCopy(node.name));
const std::string nodeName = lowerIdentifier(StringUtils::trim(node.name));
if (!nodeName.empty()) {
if (const DesktopEntry* entry = findDesktopEntryByTerm(nodeName)) {
out.entry = entry;
@@ -171,11 +171,6 @@ namespace {
localY < closeTop + kCloseButtonSize;
}
bool isBlankText(std::string_view text) {
return text.empty() ||
std::all_of(text.begin(), text.end(), [](unsigned char ch) { return std::isspace(ch) != 0; });
}
float measureActionsFromPairs(RenderContext& rc, const std::vector<std::string>& actions) {
if (actions.empty()) {
return 0.0f;
@@ -190,7 +185,7 @@ namespace {
for (std::size_t i = 0; i + 1 < actions.size() && actionCount < kMaxActionButtons; i += 2) {
const std::string& actionKey = actions[i];
std::string actionLabel = actions[i + 1];
if (isBlankText(actionLabel)) {
if (StringUtils::isBlank(actionLabel)) {
actionLabel = fallbackActionLabel();
}
if (actionKey.empty()) {
@@ -237,7 +232,7 @@ namespace {
ToastGeometry out;
if (isBlankText(displayBody)) {
if (StringUtils::isBlank(displayBody)) {
Label summaryProbe;
summaryProbe.setFontSize(kSummaryFontSize);
summaryProbe.setBold(true);
@@ -490,7 +485,7 @@ void NotificationToast::onNotificationEvent(const Notification& n, NotificationE
cs.bodyLabel->setMaxLines(std::max(1, bodyLines));
cs.bodyLabel->setText(bodyLines > 0 ? displayBody : "");
cs.bodyLabel->measure(*m_renderContext);
cs.bodyLabel->setVisible(bodyLines > 0 && !isBlankText(displayBody));
cs.bodyLabel->setVisible(bodyLines > 0 && !StringUtils::isBlank(displayBody));
cs.bodyLabel->setPosition(notificationTextStartX(), bodyTopForSummary(summaryH));
clampBodyLabelHeight(*cs.bodyLabel, bodyHeight);
}
@@ -1638,7 +1633,7 @@ InputArea* NotificationToast::buildCard(const PopupEntry& entry, Node** outCardC
for (std::size_t i = 0; i + 1 < entry.actions.size() && actionCount < kMaxActionButtons; i += 2) {
const std::string actionKey = entry.actions[i];
std::string actionLabel = entry.actions[i + 1];
if (isBlankText(actionLabel)) {
if (StringUtils::isBlank(actionLabel)) {
actionLabel = fallbackActionLabel();
}
if (actionKey.empty()) {
+3 -10
View File
@@ -5,9 +5,9 @@
#include "shell/control_center/shortcut_registry.h"
#include "theme/builtin_palettes.h"
#include "theme/builtin_templates.h"
#include "util/string_utils.h"
#include <algorithm>
#include <cctype>
#include <cmath>
#include <cstddef>
#include <string>
@@ -145,13 +145,6 @@ namespace settings {
return out;
}
std::string lower(std::string_view value) {
std::string out(value);
std::transform(out.begin(), out.end(), out.begin(),
[](unsigned char c) { return static_cast<char>(std::tolower(c)); });
return out;
}
SettingEntry makeEntry(std::string section, std::string group, std::string title, std::string subtitle,
std::vector<std::string> path, SettingControl control, std::string tags = {},
bool advanced = false) {
@@ -167,7 +160,7 @@ namespace settings {
.path = std::move(path),
.control = std::move(control),
.advanced = advanced,
.searchText = lower(searchText),
.searchText = StringUtils::toLower(searchText),
.visibleWhen = std::nullopt,
};
}
@@ -201,7 +194,7 @@ namespace settings {
return names;
}
std::string normalizedSettingQuery(std::string_view query) { return lower(query); }
std::string normalizedSettingQuery(std::string_view query) { return StringUtils::toLower(query); }
bool matchesNormalizedSettingQuery(const SettingEntry& entry, std::string_view normalizedQuery) {
if (normalizedQuery.empty()) {
+3 -9
View File
@@ -14,11 +14,11 @@
#include "ui/controls/toggle.h"
#include "ui/palette.h"
#include "ui/style.h"
#include "util/string_utils.h"
#include "wayland/wayland_connection.h"
#include "xdg-shell-client-protocol.h"
#include <algorithm>
#include <cctype>
#include <cmath>
#include <cstddef>
#include <string>
@@ -51,16 +51,10 @@ namespace settings {
return label;
}
std::string toLowerAscii(std::string text) {
std::transform(text.begin(), text.end(), text.begin(),
[](unsigned char ch) { return static_cast<char>(std::tolower(ch)); });
return text;
}
void sortSearchOptions(std::vector<SearchPickerOption>& options) {
std::sort(options.begin(), options.end(), [](const SearchPickerOption& a, const SearchPickerOption& b) {
const std::string aLabel = toLowerAscii(a.label);
const std::string bLabel = toLowerAscii(b.label);
const std::string aLabel = StringUtils::toLower(a.label);
const std::string bLabel = StringUtils::toLower(b.label);
if (aLabel == bLabel) {
return a.value < b.value;
}
+107
View File
@@ -0,0 +1,107 @@
#pragma once
#include "util/string_utils.h"
#include <algorithm>
#include <cctype>
#include <string>
#include <string_view>
#include <vector>
namespace tray {
inline std::vector<std::string> identifierVariants(std::string_view value) {
std::vector<std::string> out;
if (value.empty()) {
return out;
}
auto pushUnique = [&out](std::string candidate) {
if (candidate.empty()) {
return;
}
if (std::ranges::find(out, candidate) == out.end()) {
out.push_back(std::move(candidate));
}
};
std::string base(value);
pushUnique(base);
pushUnique(StringUtils::toLower(base));
if (const auto slash = base.find_last_of('/'); slash != std::string::npos && slash + 1 < base.size()) {
base = base.substr(slash + 1);
pushUnique(base);
pushUnique(StringUtils::toLower(base));
}
std::string dashed = base;
std::replace(dashed.begin(), dashed.end(), '_', '-');
pushUnique(dashed);
pushUnique(StringUtils::toLower(dashed));
std::string underscored = base;
std::replace(underscored.begin(), underscored.end(), '-', '_');
pushUnique(underscored);
pushUnique(StringUtils::toLower(underscored));
auto pushReducedForms = [&pushUnique](std::string candidate) {
if (candidate.empty()) {
return;
}
pushUnique(candidate);
pushUnique(StringUtils::toLower(candidate));
bool changed = true;
while (changed && !candidate.empty()) {
changed = false;
for (const auto& suffix : {"_client", "-client", ".desktop", "_indicator", "-indicator", "_tray", "-tray",
"_status", "-status", "_panel", "-panel"}) {
if (candidate.size() > std::char_traits<char>::length(suffix) && candidate.ends_with(suffix)) {
candidate = candidate.substr(0, candidate.size() - std::char_traits<char>::length(suffix));
pushUnique(candidate);
pushUnique(StringUtils::toLower(candidate));
changed = true;
break;
}
}
if (changed || candidate.empty()) {
continue;
}
const auto separator = candidate.find_last_of("-_");
if (separator != std::string::npos && separator + 1 < candidate.size()) {
const std::string tail = candidate.substr(separator + 1);
const bool numericTail = std::ranges::all_of(tail, [](unsigned char c) { return std::isdigit(c) != 0; });
if (numericTail) {
candidate = candidate.substr(0, separator);
pushUnique(candidate);
pushUnique(StringUtils::toLower(candidate));
changed = true;
continue;
}
}
for (const auto& suffix : {"-linux", "_linux"}) {
if (candidate.size() > std::char_traits<char>::length(suffix) && candidate.ends_with(suffix)) {
candidate = candidate.substr(0, candidate.size() - std::char_traits<char>::length(suffix));
pushUnique(candidate);
pushUnique(StringUtils::toLower(candidate));
changed = true;
break;
}
}
}
};
for (const auto& candidate : std::vector<std::string>{base, dashed, underscored}) {
pushReducedForms(candidate);
}
return out;
}
} // namespace tray
+5 -46
View File
@@ -8,15 +8,16 @@
#include "i18n/i18n.h"
#include "render/render_context.h"
#include "shell/panel/panel_manager.h"
#include "shell/tray/tray_identifier.h"
#include "ui/controls/context_menu.h"
#include "ui/style.h"
#include "util/string_utils.h"
#include "wayland/layer_surface.h"
#include "wayland/wayland_connection.h"
#include "wayland/wayland_seat.h"
#include "xdg-shell-client-protocol.h"
#include <algorithm>
#include <cctype>
#include <optional>
#include <string>
@@ -81,55 +82,13 @@ namespace {
return out;
}
std::string toLower(std::string_view value) {
std::string out(value);
std::transform(out.begin(), out.end(), out.begin(),
[](unsigned char c) { return static_cast<char>(std::tolower(c)); });
return out;
}
std::vector<std::string> identifierVariants(std::string_view value) {
std::vector<std::string> out;
if (value.empty()) {
return out;
}
auto pushUnique = [&out](std::string candidate) {
if (candidate.empty()) {
return;
}
if (std::ranges::find(out, candidate) == out.end()) {
out.push_back(std::move(candidate));
}
};
std::string base(value);
pushUnique(base);
pushUnique(toLower(base));
if (const auto slash = base.find_last_of('/'); slash != std::string::npos && slash + 1 < base.size()) {
base = base.substr(slash + 1);
pushUnique(base);
pushUnique(toLower(base));
}
std::string dashed = base;
std::replace(dashed.begin(), dashed.end(), '_', '-');
pushUnique(dashed);
pushUnique(toLower(dashed));
std::string underscored = base;
std::replace(underscored.begin(), underscored.end(), '-', '_');
pushUnique(underscored);
pushUnique(toLower(underscored));
return out;
}
using tray::identifierVariants;
bool tokenMatchesItem(std::string_view token, const TrayItemInfo& item) {
if (token.empty()) {
return false;
}
const auto normalizedToken = toLower(token);
const auto normalizedToken = StringUtils::toLower(token);
std::vector<std::string> candidates;
auto appendVariants = [&candidates](std::string_view text) {
@@ -157,7 +116,7 @@ namespace {
if (value.empty()) {
return true;
}
const auto lower = toLower(value);
const auto lower = StringUtils::toLower(value);
return lower.find("status_icon") != std::string::npos || lower.find("statusnotifieritem") != std::string::npos ||
lower.find("statusnotifier") != std::string::npos || lower.find("status-notifier") != std::string::npos ||
lower.find("status notifier") != std::string::npos;
+11 -27
View File
@@ -8,6 +8,7 @@
#include "dbus/system_bus.h"
#include "ipc/ipc_arg_parse.h"
#include "ipc/ipc_service.h"
#include "util/string_utils.h"
#include "wayland/wayland_connection.h"
#include <algorithm>
@@ -132,25 +133,6 @@ namespace {
return out;
}
std::string trim(std::string_view input) {
std::size_t start = 0;
while (start < input.size() && std::isspace(static_cast<unsigned char>(input[start])) != 0) {
++start;
}
std::size_t end = input.size();
while (end > start && std::isspace(static_cast<unsigned char>(input[end - 1])) != 0) {
--end;
}
return std::string(input.substr(start, end - start));
}
std::string toLower(std::string_view input) {
std::string out(input);
std::transform(out.begin(), out.end(), out.begin(),
[](unsigned char ch) { return static_cast<char>(std::tolower(ch)); });
return out;
}
int readSysfsInt(const std::string& path) {
std::ifstream file(path);
int value = -1;
@@ -337,7 +319,7 @@ namespace {
}
std::string normalizeConnectorName(std::string_view raw) {
std::string connector = trim(raw);
std::string connector = StringUtils::trim(raw);
if (connector.starts_with("card")) {
const auto dash = connector.find('-');
if (dash != std::string::npos) {
@@ -352,7 +334,7 @@ namespace {
while (start <= output.size()) {
const std::size_t end = output.find('\n', start);
const std::string line = output.substr(start, end == std::string::npos ? output.size() - start : end - start);
const std::string lower = toLower(line);
const std::string lower = StringUtils::toLower(line);
const std::size_t currentPos = lower.find("current value");
const std::size_t maxPos = lower.find("max value");
if (currentPos == std::string::npos || maxPos == std::string::npos || maxPos <= currentPos) {
@@ -520,7 +502,8 @@ namespace {
return {};
}
if (detectResult.exitCode != 0) {
kLog.warn("ddcutil detect failed with exit code {}: {}", detectResult.exitCode, trim(detectResult.output));
kLog.warn("ddcutil detect failed with exit code {}: {}", detectResult.exitCode,
StringUtils::trim(detectResult.output));
return {};
}
@@ -536,7 +519,8 @@ namespace {
current.currentRaw = brightness->first;
current.maxRaw = brightness->second;
} else {
kLog.warn("ddcutil: skipping bus {} because brightness query failed: {}", current.bus, trim(getvcpDetail));
kLog.warn("ddcutil: skipping bus {} because brightness query failed: {}", current.bus,
StringUtils::trim(getvcpDetail));
}
}
if (current.currentRaw >= 0 && current.maxRaw > 0) {
@@ -549,7 +533,7 @@ namespace {
std::size_t start = 0;
while (start <= detectResult.output.size()) {
const std::size_t end = detectResult.output.find('\n', start);
const std::string line = trim(detectResult.output.substr(
const std::string line = StringUtils::trim(detectResult.output.substr(
start, end == std::string::npos ? detectResult.output.size() - start : end - start));
if (line.starts_with("Display ")) {
flushCurrent();
@@ -566,9 +550,9 @@ namespace {
} else if (line.starts_with("DRM_connector:")) {
current.connectorName = normalizeConnectorName(line.substr(std::strlen("DRM_connector:")));
} else if (line.starts_with("Monitor:")) {
current.label = trim(line.substr(std::strlen("Monitor:")));
current.label = StringUtils::trim(line.substr(std::strlen("Monitor:")));
} else if (line.starts_with("Model:") && current.label.empty()) {
current.label = trim(line.substr(std::strlen("Model:")));
current.label = StringUtils::trim(line.substr(std::strlen("Model:")));
}
if (end == std::string::npos) {
@@ -1232,7 +1216,7 @@ struct BrightnessService::Impl {
display->failureCount, kDdcFailureCooldown.count());
} else {
kLog.warn("ddcutil {} failed for '{}': {}", completion.type == WorkerCompletion::Type::Set ? "write" : "refresh",
display->pub.id, trim(completion.detail));
display->pub.id, StringUtils::trim(completion.detail));
}
return false;
+9 -15
View File
@@ -1,6 +1,7 @@
#include "system/desktop_entry.h"
#include "core/log.h"
#include "util/string_utils.h"
#include <algorithm>
#include <cstdlib>
@@ -18,15 +19,8 @@ namespace {
constexpr Logger kLog("desktop_entry");
std::string toLower(std::string_view s) {
std::string result(s);
std::transform(result.begin(), result.end(), result.begin(),
[](unsigned char c) { return static_cast<char>(std::tolower(c)); });
return result;
}
bool parseDesktopBool(std::string_view value) {
const std::string lower = toLower(value);
const std::string lower = StringUtils::toLower(value);
return lower == "true" || lower == "1" || lower == "yes";
}
@@ -263,13 +257,13 @@ namespace {
}
// Pre-lowercase for matching
entry.nameLower = toLower(entry.name);
entry.genericNameLower = toLower(entry.genericName);
entry.keywordsLower = toLower(entry.keywords);
entry.categoriesLower = toLower(entry.categories);
entry.startupWmClassLower = toLower(entry.startupWmClass);
entry.idLower = toLower(entry.id);
entry.execLower = toLower(entry.exec);
entry.nameLower = StringUtils::toLower(entry.name);
entry.genericNameLower = StringUtils::toLower(entry.genericName);
entry.keywordsLower = StringUtils::toLower(entry.keywords);
entry.categoriesLower = StringUtils::toLower(entry.categories);
entry.startupWmClassLower = StringUtils::toLower(entry.startupWmClass);
entry.idLower = StringUtils::toLower(entry.id);
entry.execLower = StringUtils::toLower(entry.exec);
// Build actions in the declared order.
for (const auto& id : actionOrder) {
+3 -40
View File
@@ -1,6 +1,7 @@
#include "system/distro_info.h"
#include "i18n/i18n.h"
#include "util/string_utils.h"
#include <cctype>
#include <cstdint>
@@ -21,44 +22,6 @@
namespace {
std::string trim(std::string_view value) {
std::size_t start = 0;
while (start < value.size() && std::isspace(static_cast<unsigned char>(value[start])) != 0) {
++start;
}
std::size_t end = value.size();
while (end > start && std::isspace(static_cast<unsigned char>(value[end - 1])) != 0) {
--end;
}
return std::string(value.substr(start, end - start));
}
std::string unquote(std::string value) {
if (value.size() >= 2 &&
((value.front() == '"' && value.back() == '"') || (value.front() == '\'' && value.back() == '\''))) {
value = value.substr(1, value.size() - 2);
}
std::string out;
out.reserve(value.size());
bool escaping = false;
for (char ch : value) {
if (escaping) {
out.push_back(ch);
escaping = false;
continue;
}
if (ch == '\\') {
escaping = true;
continue;
}
out.push_back(ch);
}
return out;
}
std::optional<std::unordered_map<std::string, std::string>> parseOsRelease(const std::filesystem::path& path) {
std::ifstream file(path);
if (!file.is_open()) {
@@ -68,7 +31,7 @@ namespace {
std::unordered_map<std::string, std::string> values;
std::string line;
while (std::getline(file, line)) {
const auto trimmed = trim(line);
const auto trimmed = StringUtils::trim(line);
if (trimmed.empty() || trimmed.front() == '#') {
continue;
}
@@ -79,7 +42,7 @@ namespace {
}
auto key = std::string(trimmed.substr(0, eq));
auto value = unquote(trim(std::string_view(trimmed).substr(eq + 1)));
auto value = StringUtils::unquote(StringUtils::trim(std::string_view(trimmed).substr(eq + 1)));
values[std::move(key)] = std::move(value);
}
+6 -13
View File
@@ -2,6 +2,7 @@
#include "compositors/compositor_detect.h"
#include "i18n/i18n.h"
#include "util/string_utils.h"
#include <cstdlib>
#include <filesystem>
@@ -13,14 +14,6 @@
namespace {
std::string trimWhitespace(std::string s) {
while (!s.empty() && (s.back() == ' ' || s.back() == '\t' || s.back() == '\n' || s.back() == '\r')) {
s.pop_back();
}
const auto start = s.find_first_not_of(" \t");
return start == std::string::npos ? "" : s.substr(start);
}
std::string readCpuModel() {
std::ifstream file{"/proc/cpuinfo"};
if (!file.is_open()) {
@@ -32,7 +25,7 @@ namespace {
if (line.starts_with("model name")) {
const auto colonPos = line.find(':');
if (colonPos != std::string::npos) {
return trimWhitespace(line.substr(colonPos + 1));
return StringUtils::trim(line.substr(colonPos + 1));
}
}
}
@@ -61,7 +54,7 @@ namespace {
if (line.starts_with(vendorId)) {
const auto nameStart = line.find(" ");
if (nameStart != std::string::npos) {
vendorName = trimWhitespace(line.substr(nameStart));
vendorName = StringUtils::trim(line.substr(nameStart));
inVendor = true;
}
}
@@ -73,11 +66,11 @@ namespace {
}
if (line[0] == '\t' && (line.size() < 2 || line[1] != '\t')) {
auto stripped = trimWhitespace(line);
auto stripped = StringUtils::trim(line);
if (stripped.starts_with(deviceId)) {
const auto nameStart = stripped.find(" ");
if (nameStart != std::string::npos) {
auto deviceName = trimWhitespace(stripped.substr(nameStart));
auto deviceName = StringUtils::trim(stripped.substr(nameStart));
if (!deviceName.empty()) {
return deviceName;
}
@@ -99,7 +92,7 @@ namespace {
}
std::string line;
std::getline(file, line);
return trimWhitespace(line);
return StringUtils::trim(line);
}
std::string detectGpu() {
+6 -14
View File
@@ -1,5 +1,7 @@
#include "system/icon_resolver.h"
#include "util/string_utils.h"
#include <algorithm>
#include <cstdlib>
#include <filesystem>
@@ -34,17 +36,7 @@ namespace {
return state;
}
std::string trim(std::string_view value) {
while (!value.empty() && (value.front() == '\'' || value.front() == '"' || value.front() == ' ' ||
value.front() == '\t' || value.front() == '\r' || value.front() == '\n')) {
value = value.substr(1);
}
while (!value.empty() && (value.back() == '\'' || value.back() == '"' || value.back() == ' ' ||
value.back() == '\t' || value.back() == '\r' || value.back() == '\n')) {
value = value.substr(0, value.size() - 1);
}
return std::string(value);
}
std::string trimAndUnquote(std::string_view value) { return StringUtils::unquote(StringUtils::trim(value)); }
void pushUnique(std::vector<std::string>& values, std::string value) {
if (value.empty()) {
@@ -61,7 +53,7 @@ namespace {
while (start <= value.size()) {
const auto next = value.find(separator, start);
const auto part = next == std::string_view::npos ? value.substr(start) : value.substr(start, next - start);
const std::string trimmed = trim(part);
const std::string trimmed = trimAndUnquote(part);
if (!trimmed.empty()) {
parts.push_back(trimmed);
}
@@ -181,7 +173,7 @@ namespace {
return std::nullopt;
}
std::string value = trim(raw);
std::string value = trimAndUnquote(raw);
g_free(raw);
if (value.empty()) {
return std::nullopt;
@@ -217,7 +209,7 @@ namespace {
if (eq == std::string::npos) {
continue;
}
std::string value = trim(std::string_view(line.data() + eq + 1, line.size() - eq - 1));
std::string value = trimAndUnquote(std::string_view(line.data() + eq + 1, line.size() - eq - 1));
if (!value.empty()) {
candidates.emplace_back(std::move(value));
}
+6 -12
View File
@@ -1,9 +1,9 @@
#include "system/system_monitor_service.h"
#include "core/log.h"
#include "util/string_utils.h"
#include <algorithm>
#include <cctype>
#include <chrono>
#include <filesystem>
#include <fstream>
@@ -75,16 +75,10 @@ namespace {
return static_cast<double>(raw);
}
std::string toLower(std::string value) {
std::transform(value.begin(), value.end(), value.begin(),
[](unsigned char c) { return static_cast<char>(std::tolower(c)); });
return value;
}
int scoreHwmonSensor(const std::string& hwmon_name, const std::string& label) {
int score = 0;
const std::string name = toLower(hwmon_name);
const std::string lbl = toLower(label);
const std::string name = StringUtils::toLower(hwmon_name);
const std::string lbl = StringUtils::toLower(label);
if (name.find("coretemp") != std::string::npos || name.find("k10temp") != std::string::npos ||
name.find("zenpower") != std::string::npos || name.find("cpu") != std::string::npos) {
@@ -100,14 +94,14 @@ namespace {
}
bool isCpuThermalZoneType(const std::string& type) {
const std::string t = toLower(type);
const std::string t = StringUtils::toLower(type);
return t.find("x86_pkg_temp") != std::string::npos || t.find("cpu") != std::string::npos ||
t.find("soc") != std::string::npos || t.find("package") != std::string::npos;
}
int scoreGpuHwmonSensor(const std::string& hwmon_name, const std::string& label) {
const std::string name = toLower(hwmon_name);
const std::string lbl = toLower(label);
const std::string name = StringUtils::toLower(hwmon_name);
const std::string lbl = StringUtils::toLower(label);
if (name.find("nvidia") != std::string::npos) {
return -1;
+2 -13
View File
@@ -355,25 +355,14 @@ namespace noctalia::theme {
return out;
}
std::string replaceAll(std::string value, const std::string& from, const std::string& to) {
if (from.empty())
return value;
size_t pos = 0;
while ((pos = value.find(from, pos)) != std::string::npos) {
value.replace(pos, from.size(), to);
pos += to.size();
}
return value;
}
std::string applyReplace(const std::string& value, const std::optional<std::string>& arg) {
if (!arg)
return value;
std::smatch match;
if (std::regex_match(*arg, match, std::regex(R"REGEX("([^"]*?)"\s*,\s*"([^"]*?)")REGEX")))
return replaceAll(value, match[1].str(), match[2].str());
return StringUtils::replaceAll(value, match[1].str(), match[2].str());
if (std::regex_match(*arg, match, std::regex(R"REGEX('([^']*?)'\s*,\s*'([^']*?)')REGEX")))
return replaceAll(value, match[1].str(), match[2].str());
return StringUtils::replaceAll(value, match[1].str(), match[2].str());
return value;
}
+2 -9
View File
@@ -12,9 +12,9 @@
#include "ui/controls/virtual_grid_view.h"
#include "ui/palette.h"
#include "ui/style.h"
#include "util/string_utils.h"
#include <algorithm>
#include <cctype>
#include <memory>
#include <unordered_set>
@@ -37,13 +37,6 @@ namespace {
button.setRadius(Style::radiusMd * scale);
}
std::string toLowerCopy(std::string_view value) {
std::string out(value);
std::transform(out.begin(), out.end(), out.begin(),
[](unsigned char c) { return static_cast<char>(std::tolower(c)); });
return out;
}
} // namespace
class GlyphGridAdapter : public VirtualGridAdapter {
@@ -136,7 +129,7 @@ public:
}
return;
}
const std::string needle = toLowerCopy(filter);
const std::string needle = StringUtils::toLower(filter);
for (std::size_t i = 0; i < m_master.size(); ++i) {
// Names in the registry are already lowercase; no need to lower each entry.
if (m_master[i].name.find(needle) != std::string::npos) {
+3 -12
View File
@@ -16,9 +16,9 @@
#include "ui/dialogs/file_entry_row.h"
#include "ui/dialogs/file_entry_tile.h"
#include "ui/style.h"
#include "util/string_utils.h"
#include <algorithm>
#include <cctype>
#include <cmath>
#include <cstdlib>
#include <functional>
@@ -33,15 +33,6 @@ namespace {
constexpr std::size_t kGridRowOverscan = 1;
constexpr float kGridMinCellWidth = 140.0f;
std::string lower(std::string_view text) {
std::string out;
out.reserve(text.size());
for (char ch : text) {
out.push_back(static_cast<char>(std::tolower(static_cast<unsigned char>(ch))));
}
return out;
}
void configureDialogActionButton(Button& button, float scale) {
button.setMinHeight(Style::controlHeight * scale);
button.setMinWidth(92.0f * scale);
@@ -614,11 +605,11 @@ void FileDialogView::refreshDirectory() {
void FileDialogView::applyFilter(bool resetScroll) {
const std::filesystem::path preserved = selectedPath();
const std::string query = lower(m_filterQuery);
const std::string query = StringUtils::toLower(m_filterQuery);
m_visibleEntries.clear();
for (const auto& entry : m_entries) {
if (!query.empty() && lower(entry.name).find(query) == std::string::npos) {
if (!query.empty() && StringUtils::toLower(entry.name).find(query) == std::string::npos) {
continue;
}
m_visibleEntries.push_back(entry);
+117
View File
@@ -4,6 +4,7 @@
#include <cctype>
#include <string>
#include <string_view>
#include <vector>
namespace StringUtils {
@@ -154,4 +155,120 @@ namespace StringUtils {
return out;
}
[[nodiscard]] inline std::vector<std::string> splitWhitespace(std::string_view text) {
std::vector<std::string> result;
std::size_t i = 0;
while (i < text.size()) {
while (i < text.size() && std::isspace(static_cast<unsigned char>(text[i])) != 0) {
++i;
}
if (i >= text.size()) {
break;
}
std::size_t start = i;
while (i < text.size() && std::isspace(static_cast<unsigned char>(text[i])) == 0) {
++i;
}
result.emplace_back(text.substr(start, i - start));
}
return result;
}
[[nodiscard]] inline std::vector<std::string_view> split(std::string_view text, char delimiter) {
std::vector<std::string_view> result;
std::size_t start = 0;
while (start <= text.size()) {
std::size_t pos = text.find(delimiter, start);
if (pos == std::string_view::npos) {
result.push_back(text.substr(start));
break;
}
result.push_back(text.substr(start, pos - start));
start = pos + 1;
}
return result;
}
[[nodiscard]] inline std::string join(const std::vector<std::string>& parts, std::string_view separator) {
std::string result;
for (std::size_t i = 0; i < parts.size(); ++i) {
if (i > 0) {
result.append(separator);
}
result.append(parts[i]);
}
return result;
}
[[nodiscard]] inline std::string replaceAll(std::string_view input, std::string_view from, std::string_view to) {
if (from.empty()) {
return std::string(input);
}
std::string result;
result.reserve(input.size());
std::size_t pos = 0;
while (pos < input.size()) {
std::size_t found = input.find(from, pos);
if (found == std::string_view::npos) {
result.append(input.substr(pos));
break;
}
result.append(input.substr(pos, found - pos));
result.append(to);
pos = found + from.size();
}
return result;
}
[[nodiscard]] inline bool isBlank(std::string_view text) {
return text.empty() ||
std::all_of(text.begin(), text.end(), [](unsigned char ch) { return std::isspace(ch) != 0; });
}
[[nodiscard]] inline std::string shellQuote(std::string_view text) {
std::string result = "'";
for (char ch : text) {
if (ch == '\'') {
result += "'\\''";
} else {
result += ch;
}
}
result += '\'';
return result;
}
[[nodiscard]] inline std::string quoteDouble(std::string_view text) {
std::string result = "\"";
for (char ch : text) {
if (ch == '\\' || ch == '"') {
result += '\\';
}
result += ch;
}
result += '"';
return result;
}
[[nodiscard]] inline std::string unquote(std::string_view text) {
if (text.size() < 2) {
return std::string(text);
}
char front = text.front();
char back = text.back();
if ((front == '"' && back == '"') || (front == '\'' && back == '\'')) {
text = text.substr(1, text.size() - 2);
std::string result;
result.reserve(text.size());
for (std::size_t i = 0; i < text.size(); ++i) {
if (text[i] == '\\' && i + 1 < text.size()) {
++i;
}
result += text[i];
}
return result;
}
return std::string(text);
}
} // namespace StringUtils