diff options
| author | Eugen Wissner <belka@caraus.de> | 2025-04-19 14:48:02 +0200 |
|---|---|---|
| committer | Eugen Wissner <belka@caraus.de> | 2025-04-19 14:48:02 +0200 |
| commit | d10c5c980c18216db3bb9fb1137866cfd4f836ec (patch) | |
| tree | 79d029c2ccb61e3adfc1113eacb1f141499ace33 /cli | |
| download | katja-d10c5c980c18216db3bb9fb1137866cfd4f836ec.tar.gz | |
Initial commit
Diffstat (limited to 'cli')
| -rw-r--r-- | cli/CMakeLists.txt | 26 | ||||
| -rw-r--r-- | cli/component.cpp | 152 | ||||
| -rw-r--r-- | cli/component.hpp | 76 | ||||
| -rw-r--r-- | cli/main.cpp | 32 |
4 files changed, 286 insertions, 0 deletions
diff --git a/cli/CMakeLists.txt b/cli/CMakeLists.txt new file mode 100644 index 0000000..aaf6ebe --- /dev/null +++ b/cli/CMakeLists.txt @@ -0,0 +1,26 @@ +FetchContent_Declare(ftxui + GIT_REPOSITORY https://github.com/ArthurSonzogni/ftxui + GIT_TAG v6.0.2 + GIT_PROGRESS TRUE + GIT_SHALLOW TRUE + EXCLUDE_FROM_ALL +) +FetchContent_MakeAvailable(ftxui) + +FetchContent_Declare(toml11 + GIT_REPOSITORY https://github.com/ToruNiina/toml11.git + GIT_TAG v4.4.0 + GIT_PROGRESS TRUE +) +FetchContent_MakeAvailable(toml11) + +add_executable(katja-cli main.cpp component.hpp component.cpp) +target_include_directories(katja-cli PRIVATE ${Boost_INCLUDE_DIR}) +target_link_libraries(katja-cli + LINK_PUBLIC katja + LINK_PRIVATE ftxui::screen + LINK_PRIVATE ftxui::dom + LINK_PRIVATE ftxui::component + LINK_PRIVATE toml11::toml11 +) +set_target_properties(katja-cli PROPERTIES RUNTIME_OUTPUT_NAME katja) diff --git a/cli/component.cpp b/cli/component.cpp new file mode 100644 index 0000000..ef93e87 --- /dev/null +++ b/cli/component.cpp @@ -0,0 +1,152 @@ +#include "component.hpp" + +#include <algorithm> + +namespace katja +{ + ScreenContainer::ScreenContainer(std::vector<std::pair<std::string, Page>> pages, std::function<void()> on_enter) + : on_enter(on_enter) + { + ftxui::Components menu_pages; + + std::transform(std::cbegin(pages), std::cend(pages), std::back_inserter(menu_entries), + [](const std::pair<std::string, Page>& pair) { return pair.first; }); + std::transform(std::cbegin(pages), std::cend(pages), std::back_inserter(this->menu_pages), + [](const std::pair<std::string, Page>& pair) { return pair.second; }); + std::copy(std::cbegin(this->menu_pages), std::cend(this->menu_pages), std::back_inserter(menu_pages)); + + ftxui::MenuOption menu_option = ftxui::MenuOption::Horizontal(); + this->menu = ftxui::Toggle(&this->menu_entries, &this->menu_selected); + + this->content = ftxui::Container::Tab(std::move(menu_pages), &this->menu_selected); + } + + ftxui::Element ScreenContainer::OnRender() + { + return ftxui::vbox({ + this->menu->Render(), + ftxui::separator(), + this->content->Render() + }); + } + + bool ScreenContainer::OnEvent(ftxui::Event event) + { + if (event == ftxui::Event::CtrlQ && this->on_enter) + { + on_enter(); + return true; + } + int previously = this->menu_selected; + bool result{ false }; + + if (event == ftxui::Event::CtrlP) + { + result = menu->OnEvent(ftxui::Event::ArrowLeft); + } + else if (event == ftxui::Event::CtrlN) + { + result = menu->OnEvent(ftxui::Event::ArrowRight); + } + if (previously != this->menu_selected) + { + this->menu_pages.at(this->menu_selected)->Load(); + } + if (!result) + { + result = this->menu_pages.at(this->menu_selected)->OnEvent(event); + } + return result; + } + + ftxui::Component Screen(std::vector<std::pair<std::string, Page>> pages, std::function<void()> on_enter) + { + return std::make_shared<ScreenContainer>(std::move(pages), on_enter); + } + + ftxui::Element WelcomePage::OnRender() + { + return ftxui::paragraph("Select an action in the menu."); + } + + void WelcomePage::Load() + { + } + + UpdatesPage::UpdatesPage(std::shared_ptr<struct repository> repository, package_database database) + : repository(repository), database(database) + { + } + + void UpdatesPage::Load() + { + this->updatable = repository->get_updates(this->database); + } + + ftxui::Element UpdatesPage::OnRender() + { + std::vector<ftxui::Element> lines; + + for (const auto& package_identifier : this->updatable) + { + auto line = ftxui::text(package_identifier.to_string()) | color(ftxui::Color::SkyBlue2); + lines.push_back(line); + } + ftxui::Element summary = ftxui::text(" Updates (" + std::to_string(lines.size()) + ")"); + + return ftxui::window(summary, ftxui::vbox(lines)); + } + + SearchPage::SearchPage(std::shared_ptr<struct repository> repository, const std::string& architecture) + : repository(repository), architecture(architecture) + { + ftxui::InputOption search_input_option = { .multiline = false }; + this->search_input = ftxui::Input(&this->needle, "Search", search_input_option); + this->type_input = ftxui::Radiobox(std::vector<std::string>{ "Names", "Description" }, &this->search_type); + } + + void SearchPage::Load() + { + this->needle.erase(); + } + + ftxui::Element SearchPage::OnRender() + { + std::vector<ftxui::Element> lines; + + for (const auto& package_identifier : this->search_results) + { + auto line = ftxui::text(package_identifier.to_string()) | color(ftxui::Color::SkyBlue2); + lines.push_back(line); + } + ftxui::FlexboxConfig config; + config.justify_content = ftxui::FlexboxConfig::JustifyContent::FlexStart; + config.align_items = ftxui::FlexboxConfig::AlignItems::FlexStart; + config.direction = ftxui::FlexboxConfig::Direction::Row; + + return ftxui::vbox({ + ftxui::flexbox({ + this->search_input->Render(), + ftxui::window(ftxui::text("Search in"), type_input->Render()) + }, config), + ftxui::vbox(lines) + }); + } + + bool SearchPage::OnEvent(ftxui::Event event) + { + if (event == ftxui::Event::Return) + { + if (!this->needle.empty()) + { + this->search_results = this->repository->search_names(this->architecture, this->needle); + } + return true; + } + else + { + return this->search_input->OnEvent(event); + } + return false; + } +} diff --git a/cli/component.hpp b/cli/component.hpp new file mode 100644 index 0000000..c419020 --- /dev/null +++ b/cli/component.hpp @@ -0,0 +1,76 @@ +#pragma once + +#include <ftxui/component/event.hpp> +#include <ftxui/component/component_base.hpp> +#include <ftxui/component/component.hpp> + +#include "katja/repository.hpp" +#include "katja/database.hpp" + +namespace katja +{ + class PageBase : public ftxui::ComponentBase + { + public: + virtual void Load() = 0; + }; + + using Page = std::shared_ptr<PageBase>; + using Pages = std::vector<Page>; + + class ScreenContainer final : public ftxui::ComponentBase + { + int menu_selected{ 0 }; + ftxui::Component menu; + ftxui::Component content; + std::vector<std::string> menu_entries; + Pages menu_pages; + std::function<void()> on_enter; + + public: + ScreenContainer(std::vector<std::pair<std::string, Page>> pages, std::function<void()> on_enter); + + ftxui::Element OnRender() override; + bool OnEvent(ftxui::Event event) override; + }; + + ftxui::Component Screen(std::vector<std::pair<std::string, Page>> pages, std::function<void()> on_enter); + + class WelcomePage final : public PageBase + { + public: + void Load() override; + ftxui::Element OnRender() override; + }; + + class UpdatesPage final : public PageBase + { + std::vector<package_identifier> updatable; + std::shared_ptr<struct repository> repository; + package_database database; + + public: + UpdatesPage(std::shared_ptr<struct repository> repository, package_database database); + + void Load() override; + ftxui::Element OnRender() override; + }; + + class SearchPage final : public PageBase + { + std::string needle; + ftxui::Component search_input; + ftxui::Component type_input; + std::shared_ptr<struct repository> repository; + std::string architecture; + std::vector<package_identifier> search_results; + int search_type{ 0 }; + + public: + SearchPage(std::shared_ptr<struct repository> repository, const std::string& architecture); + + void Load() override; + ftxui::Element OnRender() override; + bool OnEvent(ftxui::Event event) override; + }; +} diff --git a/cli/main.cpp b/cli/main.cpp new file mode 100644 index 0000000..a204d2f --- /dev/null +++ b/cli/main.cpp @@ -0,0 +1,32 @@ +#include <filesystem> + +#include <ftxui/component/screen_interactive.hpp> +#include <ftxui/dom/elements.hpp> +#include <toml.hpp> + +#include "katja/sbo.hpp" +#include "katja/database.hpp" +#include "component.hpp" + +int main(int argc, const char **argv) +{ + auto configuration = toml::parse("katja.toml"); + katja::package_database installed_database = katja::read_installed_database(); + + for (const auto& [repository_name, repository_value] : configuration.as_table()) + { + std::filesystem::path slackbuild_repository{ repository_value.at("path").as_string() }; + auto repository = std::make_shared<katja::sbo_repository>(slackbuild_repository); + + auto screen = ftxui::ScreenInteractive::Fullscreen(); + + auto container = Screen(std::vector<std::pair<std::string, katja::Page>>{ + { "Home", ftxui::Make<katja::WelcomePage>() }, + { "Updates", ftxui::Make<katja::UpdatesPage>(repository, std::move(installed_database)) }, + { "Search", ftxui::Make<katja::SearchPage>(repository, "x86-64") } + }, screen.ExitLoopClosure()); + + screen.Loop(container); + } + return EXIT_SUCCESS; +} |
