lots of new stuff waow
This commit is contained in:
parent
bb4ccb5d42
commit
867bab1050
38 changed files with 704 additions and 452 deletions
368
src/UI/UI.cpp
Normal file
368
src/UI/UI.cpp
Normal file
|
@ -0,0 +1,368 @@
|
|||
#include "UI.hpp"
|
||||
|
||||
#include "Util/Types.hpp"
|
||||
|
||||
namespace ui {
|
||||
using namespace ftxui;
|
||||
using namespace util::types;
|
||||
|
||||
constexpr Theme DEFAULT_THEME = {
|
||||
.icon = Color::Cyan,
|
||||
.label = Color::Yellow,
|
||||
.value = Color::White,
|
||||
.border = Color::GrayLight,
|
||||
};
|
||||
|
||||
[[maybe_unused]] static constexpr Icons NONE = {
|
||||
.user = "",
|
||||
.palette = "",
|
||||
.calendar = "",
|
||||
.host = "",
|
||||
.kernel = "",
|
||||
.os = "",
|
||||
.memory = "",
|
||||
.weather = "",
|
||||
.music = "",
|
||||
.disk = "",
|
||||
.shell = "",
|
||||
.package = "",
|
||||
.desktop = "",
|
||||
.windowManager = "",
|
||||
};
|
||||
|
||||
[[maybe_unused]] static constexpr Icons NERD = {
|
||||
.user = " ",
|
||||
.palette = " ",
|
||||
.calendar = " ",
|
||||
.host = " ",
|
||||
.kernel = " ",
|
||||
#ifdef __linux__
|
||||
.os = " ",
|
||||
#elifdef __APPLE__
|
||||
.os = " ",
|
||||
#elifdef _WIN32
|
||||
.os = " ",
|
||||
#elifdef __FreeBSD__
|
||||
.os = " ",
|
||||
#else
|
||||
.os = " ",
|
||||
#endif
|
||||
.memory = " ",
|
||||
.weather = " ",
|
||||
.music = " ",
|
||||
.disk = " ",
|
||||
.shell = " ",
|
||||
.package = " ",
|
||||
.desktop = " ",
|
||||
.windowManager = " ",
|
||||
};
|
||||
|
||||
[[maybe_unused]] static constexpr Icons EMOJI = {
|
||||
.user = " 👤 ",
|
||||
.palette = " 🎨 ",
|
||||
.calendar = " 📅 ",
|
||||
.host = " 💻 ",
|
||||
.kernel = " 🫀 ",
|
||||
.os = " 🤖 ",
|
||||
.memory = " 🧠 ",
|
||||
.weather = " 🌈 ",
|
||||
.music = " 🎵 ",
|
||||
.disk = " 💾 ",
|
||||
.shell = " 💲 ",
|
||||
.package = " 📦 ",
|
||||
.desktop = " 🖥️ ",
|
||||
.windowManager = " 🪟 ",
|
||||
};
|
||||
|
||||
constexpr inline Icons ICON_TYPE = NERD;
|
||||
|
||||
struct RowInfo {
|
||||
StringView icon;
|
||||
StringView label;
|
||||
String value;
|
||||
};
|
||||
|
||||
namespace {
|
||||
#ifdef __linux__
|
||||
// clang-format off
|
||||
constexpr Array<Pair<String, String>, 13> distro_icons {{
|
||||
{ "NixOS", " " },
|
||||
{ "Zorin", " " },
|
||||
{ "Debian", " " },
|
||||
{ "Fedora", " " },
|
||||
{ "Gentoo", " " },
|
||||
{ "Ubuntu", " " },
|
||||
{ "Manjaro", " " },
|
||||
{ "Pop!_OS", " " },
|
||||
{ "Arch Linux", " " },
|
||||
{ "Linux Mint", " " },
|
||||
{ "Void Linux", " " },
|
||||
{ "Alpine Linux", " " },
|
||||
}};
|
||||
// clang-format on
|
||||
|
||||
fn GetDistroIcon() -> Option<StringView> {
|
||||
using namespace matchit;
|
||||
|
||||
const Result<String> distro = os::GetOSVersion();
|
||||
|
||||
for (const auto& [distroName, distroIcon] : distro_icons)
|
||||
if (distro->contains(distroName))
|
||||
return distroIcon;
|
||||
|
||||
return None;
|
||||
}
|
||||
#endif
|
||||
|
||||
fn CreateColorCircles() -> Element {
|
||||
auto colorView =
|
||||
std::views::iota(0, 16) | std::views::transform([](i32 colorIndex) {
|
||||
return ftxui::hbox(
|
||||
{
|
||||
ftxui::text("◯") | ftxui::bold | ftxui::color(static_cast<ftxui::Color::Palette256>(colorIndex)),
|
||||
ftxui::text(" "),
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
return hbox(Elements(std::ranges::begin(colorView), std::ranges::end(colorView)));
|
||||
}
|
||||
|
||||
fn get_visual_width(const String& str) -> usize {
|
||||
return ftxui::string_width(str);
|
||||
}
|
||||
|
||||
fn get_visual_width_sv(const StringView& sview) -> usize {
|
||||
return ftxui::string_width(String(sview));
|
||||
}
|
||||
|
||||
fn find_max_label_len(const std::vector<RowInfo>& rows) -> usize {
|
||||
usize maxWidth = 0;
|
||||
for (const RowInfo& row : rows) maxWidth = std::max(maxWidth, get_visual_width_sv(row.label));
|
||||
|
||||
return maxWidth;
|
||||
};
|
||||
|
||||
fn CreateInfoBox(const Config& config, const os::SystemData& data) -> Element {
|
||||
const String& name = config.general.name;
|
||||
const Weather& weather = config.weather;
|
||||
|
||||
// clang-format off
|
||||
const auto& [
|
||||
userIcon,
|
||||
paletteIcon,
|
||||
calendarIcon,
|
||||
hostIcon,
|
||||
kernelIcon,
|
||||
osIcon,
|
||||
memoryIcon,
|
||||
weatherIcon,
|
||||
musicIcon,
|
||||
diskIcon,
|
||||
shellIcon,
|
||||
packageIcon,
|
||||
deIcon,
|
||||
wmIcon
|
||||
] = ui::ICON_TYPE;
|
||||
// clang-format on
|
||||
|
||||
std::vector<RowInfo> initialRows; // Date, Weather
|
||||
std::vector<RowInfo> systemInfoRows; // Host, Kernel, OS, RAM, Disk, Shell, Packages
|
||||
std::vector<RowInfo> envInfoRows; // DE, WM
|
||||
|
||||
if (data.date)
|
||||
initialRows.push_back({ .icon = calendarIcon, .label = "Date", .value = *data.date });
|
||||
|
||||
if (weather.enabled && data.weather) {
|
||||
const weather::WeatherReport& weatherInfo = *data.weather;
|
||||
|
||||
String weatherValue = weather.showTownName && weatherInfo.name
|
||||
? std::format("{}°F in {}", std::lround(weatherInfo.temperature), *weatherInfo.name)
|
||||
: std::format("{}°F, {}", std::lround(weatherInfo.temperature), weatherInfo.description);
|
||||
|
||||
initialRows.push_back({ .icon = weatherIcon, .label = "Weather", .value = std::move(weatherValue) });
|
||||
} else if (weather.enabled && !data.weather.has_value())
|
||||
debug_at(data.weather.error());
|
||||
|
||||
if (data.host && !data.host->empty())
|
||||
systemInfoRows.push_back({ .icon = hostIcon, .label = "Host", .value = *data.host });
|
||||
|
||||
if (data.osVersion) {
|
||||
systemInfoRows.push_back({
|
||||
#ifdef __linux__
|
||||
.icon = GetDistroIcon().value_or(osIcon),
|
||||
#else
|
||||
.icon = osIcon,
|
||||
#endif
|
||||
.label = "OS",
|
||||
.value = *data.osVersion,
|
||||
});
|
||||
}
|
||||
|
||||
if (data.kernelVersion)
|
||||
systemInfoRows.push_back({ .icon = kernelIcon, .label = "Kernel", .value = *data.kernelVersion });
|
||||
|
||||
if (data.memInfo)
|
||||
systemInfoRows.push_back({ .icon = memoryIcon, .label = "RAM", .value = std::format("{}", BytesToGiB(*data.memInfo)) });
|
||||
else if (!data.memInfo.has_value())
|
||||
debug_at(data.memInfo.error());
|
||||
|
||||
if (data.diskUsage)
|
||||
systemInfoRows.push_back(
|
||||
{
|
||||
.icon = diskIcon,
|
||||
.label = "Disk",
|
||||
.value = std::format("{}/{}", BytesToGiB(data.diskUsage->usedBytes), BytesToGiB(data.diskUsage->totalBytes)),
|
||||
}
|
||||
);
|
||||
|
||||
if (data.shell)
|
||||
systemInfoRows.push_back({ .icon = shellIcon, .label = "Shell", .value = *data.shell });
|
||||
|
||||
if (data.packageCount) {
|
||||
if (*data.packageCount > 0)
|
||||
systemInfoRows.push_back({ .icon = packageIcon, .label = "Packages", .value = std::format("{}", *data.packageCount) });
|
||||
else
|
||||
debug_log("Package count is 0, skipping");
|
||||
}
|
||||
|
||||
bool addedDe = false;
|
||||
|
||||
if (data.desktopEnv && (!data.windowMgr || *data.desktopEnv != *data.windowMgr)) {
|
||||
envInfoRows.push_back({ .icon = deIcon, .label = "DE", .value = *data.desktopEnv });
|
||||
addedDe = true;
|
||||
}
|
||||
|
||||
if (data.windowMgr)
|
||||
if (!addedDe || (data.desktopEnv && *data.desktopEnv != *data.windowMgr))
|
||||
envInfoRows.push_back({ .icon = wmIcon, .label = "WM", .value = *data.windowMgr });
|
||||
|
||||
bool nowPlayingActive = false;
|
||||
String npText;
|
||||
|
||||
if (config.nowPlaying.enabled && data.nowPlaying) {
|
||||
const String title = data.nowPlaying->title.value_or("Unknown Title");
|
||||
const String artist = data.nowPlaying->artist.value_or("Unknown Artist");
|
||||
npText = artist + " - " + title;
|
||||
nowPlayingActive = true;
|
||||
}
|
||||
|
||||
usize maxContentWidth = 0;
|
||||
|
||||
const usize greetingWidth = get_visual_width_sv(userIcon) + get_visual_width_sv("Hello ") + get_visual_width(name) + get_visual_width_sv("! ");
|
||||
maxContentWidth = std::max(maxContentWidth, greetingWidth);
|
||||
|
||||
const usize paletteWidth = get_visual_width_sv(userIcon) + (16 * (get_visual_width_sv("◯") + get_visual_width_sv(" ")));
|
||||
maxContentWidth = std::max(maxContentWidth, paletteWidth);
|
||||
|
||||
const usize iconActualWidth = get_visual_width_sv(userIcon);
|
||||
|
||||
const usize maxLabelWidthInitial = find_max_label_len(initialRows);
|
||||
const usize maxLabelWidthSystem = find_max_label_len(systemInfoRows);
|
||||
const usize maxLabelWidthEnv = find_max_label_len(envInfoRows);
|
||||
|
||||
const usize requiredWidthInitialW = iconActualWidth + maxLabelWidthInitial;
|
||||
const usize requiredWidthSystemW = iconActualWidth + maxLabelWidthSystem;
|
||||
const usize requiredWidthEnvW = iconActualWidth + maxLabelWidthEnv;
|
||||
|
||||
fn calculateRowVisualWidth = [&](const RowInfo& row, const usize requiredLabelVisualWidth) -> usize {
|
||||
return requiredLabelVisualWidth + get_visual_width(row.value) + get_visual_width_sv(" ");
|
||||
};
|
||||
|
||||
for (const RowInfo& row : initialRows)
|
||||
maxContentWidth = std::max(maxContentWidth, calculateRowVisualWidth(row, requiredWidthInitialW));
|
||||
|
||||
for (const RowInfo& row : systemInfoRows)
|
||||
maxContentWidth = std::max(maxContentWidth, calculateRowVisualWidth(row, requiredWidthSystemW));
|
||||
|
||||
for (const RowInfo& row : envInfoRows)
|
||||
maxContentWidth = std::max(maxContentWidth, calculateRowVisualWidth(row, requiredWidthEnvW));
|
||||
|
||||
const usize targetBoxWidth = maxContentWidth + 2;
|
||||
|
||||
usize npFixedWidthLeft = 0;
|
||||
usize npFixedWidthRight = 0;
|
||||
|
||||
if (nowPlayingActive) {
|
||||
npFixedWidthLeft = get_visual_width_sv(musicIcon) + get_visual_width_sv("Playing") + get_visual_width_sv(" ");
|
||||
npFixedWidthRight = get_visual_width_sv(" ");
|
||||
}
|
||||
|
||||
i32 paragraphLimit = 1;
|
||||
|
||||
if (nowPlayingActive) {
|
||||
i32 availableForParagraph = static_cast<i32>(targetBoxWidth) - static_cast<i32>(npFixedWidthLeft) - static_cast<i32>(npFixedWidthRight);
|
||||
|
||||
availableForParagraph -= 2;
|
||||
|
||||
paragraphLimit = std::max(1, availableForParagraph);
|
||||
}
|
||||
|
||||
fn createStandardRow = [&](const RowInfo& row, const usize sectionRequiredVisualWidth) {
|
||||
return hbox(
|
||||
{
|
||||
hbox(
|
||||
{
|
||||
text(String(row.icon)) | color(ui::DEFAULT_THEME.icon),
|
||||
text(String(row.label)) | color(ui::DEFAULT_THEME.label),
|
||||
}
|
||||
) |
|
||||
size(WIDTH, EQUAL, static_cast<int>(sectionRequiredVisualWidth)),
|
||||
filler(),
|
||||
text(row.value) | color(ui::DEFAULT_THEME.value),
|
||||
text(" "),
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
Elements content;
|
||||
|
||||
content.push_back(text(String(userIcon) + "Hello " + name + "! ") | bold | color(Color::Cyan));
|
||||
content.push_back(separator() | color(ui::DEFAULT_THEME.border));
|
||||
content.push_back(hbox({ text(String(paletteIcon)) | color(ui::DEFAULT_THEME.icon), CreateColorCircles() }));
|
||||
|
||||
const bool section1Present = !initialRows.empty();
|
||||
const bool section2Present = !systemInfoRows.empty();
|
||||
const bool section3Present = !envInfoRows.empty();
|
||||
|
||||
if (section1Present)
|
||||
content.push_back(separator() | color(ui::DEFAULT_THEME.border));
|
||||
|
||||
for (const RowInfo& row : initialRows) content.push_back(createStandardRow(row, requiredWidthInitialW));
|
||||
|
||||
if ((section1Present && (section2Present || section3Present)) || (!section1Present && section2Present))
|
||||
content.push_back(separator() | color(ui::DEFAULT_THEME.border));
|
||||
|
||||
for (const RowInfo& row : systemInfoRows) content.push_back(createStandardRow(row, requiredWidthSystemW));
|
||||
|
||||
if (section2Present && section3Present)
|
||||
content.push_back(separator() | color(ui::DEFAULT_THEME.border));
|
||||
|
||||
for (const RowInfo& row : envInfoRows) content.push_back(createStandardRow(row, requiredWidthEnvW));
|
||||
|
||||
if ((section1Present || section2Present || section3Present) && nowPlayingActive)
|
||||
content.push_back(separator() | color(ui::DEFAULT_THEME.border));
|
||||
|
||||
if (nowPlayingActive) {
|
||||
content.push_back(hbox(
|
||||
{
|
||||
text(String(musicIcon)) | color(ui::DEFAULT_THEME.icon),
|
||||
text("Playing") | color(ui::DEFAULT_THEME.label),
|
||||
text(" "),
|
||||
filler(),
|
||||
paragraphAlignRight(npText) | color(Color::Magenta) | size(WIDTH, LESS_THAN, paragraphLimit),
|
||||
text(" "),
|
||||
}
|
||||
));
|
||||
}
|
||||
|
||||
return vbox(content) | borderRounded | color(Color::White);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
fn CreateUI(const Config& config, const os::SystemData& data) -> Element {
|
||||
Element infoBox = CreateInfoBox(config, data);
|
||||
|
||||
return hbox({ infoBox, filler() });
|
||||
}
|
||||
} // namespace ui
|
45
src/UI/UI.hpp
Normal file
45
src/UI/UI.hpp
Normal file
|
@ -0,0 +1,45 @@
|
|||
#pragma once
|
||||
|
||||
#include <ftxui/dom/elements.hpp> // ftxui::Element
|
||||
#include <ftxui/screen/color.hpp> // ftxui::Color
|
||||
|
||||
#include "Config/Config.hpp"
|
||||
#include "Core/SystemData.hpp"
|
||||
#include "Util/Types.hpp"
|
||||
|
||||
namespace ui {
|
||||
struct Theme {
|
||||
ftxui::Color::Palette16 icon;
|
||||
ftxui::Color::Palette16 label;
|
||||
ftxui::Color::Palette16 value;
|
||||
ftxui::Color::Palette16 border;
|
||||
};
|
||||
|
||||
extern const Theme DEFAULT_THEME;
|
||||
|
||||
struct Icons {
|
||||
util::types::StringView user;
|
||||
util::types::StringView palette;
|
||||
util::types::StringView calendar;
|
||||
util::types::StringView host;
|
||||
util::types::StringView kernel;
|
||||
util::types::StringView os;
|
||||
util::types::StringView memory;
|
||||
util::types::StringView weather;
|
||||
util::types::StringView music;
|
||||
util::types::StringView disk;
|
||||
util::types::StringView shell;
|
||||
util::types::StringView package;
|
||||
util::types::StringView desktop;
|
||||
util::types::StringView windowManager;
|
||||
};
|
||||
|
||||
extern const Icons ICON_TYPE;
|
||||
|
||||
/**
|
||||
* @brief Creates the main UI element based on system data and configuration.
|
||||
* @param config The application configuration.
|
||||
* @param data The collected system data. @return The root ftxui::Element for rendering.
|
||||
*/
|
||||
fn CreateUI(const Config& config, const os::SystemData& data) -> ftxui::Element;
|
||||
} // namespace ui
|
Loading…
Add table
Add a link
Reference in a new issue