👷 update ui

use ranges from c++ 20
fix some bugs
This commit is contained in:
Vladislav Nepogodin 2021-12-13 03:22:03 +04:00
parent cd713b8e13
commit 1fda6ae4a9
No known key found for this signature in database
GPG Key ID: B62C3D10C54D5DA9
14 changed files with 591 additions and 178 deletions

6
.gitignore vendored
View File

@ -6,6 +6,12 @@
build
cmake-build-release
cmake-build-debug
subprojects/cpr
subprojects/fmt
subprojects/ftxui
subprojects/nlohmann_json-*
subprojects/spdlog-*
subprojects/packagecache
# Prerequisites
*.d

View File

@ -37,7 +37,7 @@ pkg_check_modules(
FetchContent_Declare(ftxui
GIT_REPOSITORY "https://github.com/arthursonzogni/ftxui.git"
GIT_TAG "cecd54df42dd66fdf8386ed461e16b725bffc827"
GIT_TAG "602392c43d8bc2272c6548ae75b8802ab7ad0b2a"
)
FetchContent_MakeAvailable(ftxui)
@ -49,7 +49,7 @@ FetchContent_MakeAvailable(spdlog)
FetchContent_Declare(fmt
GIT_REPOSITORY "https://github.com/fmtlib/fmt.git"
GIT_TAG "491ba2dda5a04c2438abb6bd90219de773793ec0"
GIT_TAG "3a951a66cb0fb53ff5a9d5ce9c77e05ef9d382ce"
)
FetchContent_MakeAvailable(fmt)
@ -61,7 +61,7 @@ FetchContent_MakeAvailable(nlohmann_json)
FetchContent_Declare(cpr
GIT_REPOSITORY "https://github.com/libcpr/cpr.git"
GIT_TAG "f229f82c1b38febebbf4e9958cebcd64caa32947"
GIT_TAG "beb9e98806bb84bcc130a2cebfbcbbc6ce62b335"
)
FetchContent_MakeAvailable(cpr)
@ -89,6 +89,7 @@ add_executable(${PROJECT_NAME}
src/screen_service.hpp src/screen_service.cpp
src/config.cpp src/config.hpp
src/utils.cpp src/utils.hpp
src/widgets.cpp src/widgets.hpp
src/tui.cpp src/tui.hpp
src/main.cpp
)
@ -96,6 +97,7 @@ add_executable(${PROJECT_NAME}
add_executable(test-exec-interactive
src/config.cpp
src/utils.cpp
src/widgets.cpp src/widgets.hpp
src/tui.cpp src/tui.hpp
src/main_test.cpp
)

View File

@ -46,6 +46,7 @@ src_files = files(
'src/screen_service.hpp', 'src/screen_service.cpp',
'src/config.cpp', 'src/config.hpp',
'src/utils.cpp', 'src/utils.hpp',
'src/widgets.cpp', 'src/widgets.hpp',
'src/tui.cpp', 'src/tui.hpp',
'src/main.cpp',
)

View File

@ -2,10 +2,12 @@
#include "config.hpp"
#include "definitions.hpp"
#include "utils.hpp"
#include "widgets.hpp"
/* clang-format off */
#include <csignal> // for raise
#include <algorithm> // for transform
#include <filesystem> // for exists, is_directory
#include <memory> // for __shared_ptr_access
#include <string> // for basic_string
#include <ftxui/component/captured_mouse.hpp> // for ftxui
@ -16,113 +18,33 @@
/* clang-format on */
using namespace ftxui;
namespace fs = std::filesystem;
namespace tui {
ftxui::Element centered_widget(ftxui::Component& container, const std::string_view& title, const ftxui::Element& widget) {
return vbox({
// -------- Title --------------
text(title.data()) | bold,
filler(),
// -------- Center Menu --------------
hbox({
filler(),
border(vbox({
widget,
separator(),
container->Render() | hcenter | size(HEIGHT, LESS_THAN, 3) | size(WIDTH, GREATER_THAN, 25),
})),
filler(),
}) | center,
filler(),
});
}
// Revised to deal with partion sizes now being displayed to the user
bool confirm_mount([[maybe_unused]] const std::string_view& part_user) {
#ifdef NDEVENV
const auto& ret_status = utils::exec(fmt::format("mount | grep {}", part_user), true);
if (ret_status != "0") {
detail::infobox_widget("\nMount Failed!\n");
std::this_thread::sleep_for(std::chrono::seconds(2));
return false;
}
#endif
// auto* config_instance = Config::instance();
// auto& config_data = config_instance->data();
// const auto& partition = std::get<std::string>(config_data["PARTITION"]);
// auto& partitions = std::get<std::vector<std::string>>(config_data["PARTITIONS"]);
ftxui::Component controls_widget(const std::array<std::string_view, 2>&& titles, const std::array<std::function<void()>, 2>&& callbacks, ftxui::ButtonOption* button_option) {
/* clang-format off */
auto button_ok = Button(titles[0].data(), callbacks[0], button_option);
auto button_quit = Button(titles[1].data(), callbacks[1], button_option);
/* clang-format on */
detail::infobox_widget("\nMount Successful!\n");
std::this_thread::sleep_for(std::chrono::seconds(2));
auto container = Container::Horizontal({
button_ok,
Renderer([] { return filler() | size(WIDTH, GREATER_THAN, 3); }),
button_quit,
});
return container;
}
ftxui::Element centered_interative_multi(const std::string_view& title, ftxui::Component& widgets) {
return vbox({
// -------- Title --------------
text(title.data()) | bold,
filler(),
// -------- Center Menu --------------
hbox({
filler(),
border(vbox({
widgets->Render(),
})),
filler(),
}) | center,
filler(),
});
}
ftxui::Element multiline_text(const std::vector<std::string>& lines) {
Elements multiline;
std::transform(lines.cbegin(), lines.cend(), std::back_inserter(multiline),
[=](const std::string& line) -> Element { return text(line); });
return vbox(std::move(multiline)) | frame;
}
void msgbox_widget(const std::string_view& content, Decorator boxsize = size(HEIGHT, GREATER_THAN, 5)) {
auto screen = ScreenInteractive::Fullscreen();
/* clang-format off */
auto button_option = ButtonOption();
button_option.border = false;
auto button_back = Button("OK", screen.ExitLoopClosure(), &button_option);
/* clang-format on */
auto container = Container::Horizontal({
button_back,
});
std::string tmp{content.data()};
auto renderer = Renderer(container, [&] {
return tui::centered_widget(container, "New CLI Installer", multiline_text(utils::make_multiline(tmp)) | hcenter | boxsize);
});
screen.Loop(renderer);
}
void menu_widget(const std::vector<std::string>& entries, const std::function<void()>&& ok_callback, std::int32_t* selected) {
auto screen = ScreenInteractive::Fullscreen();
auto menu = Menu(&entries, selected);
auto content = Renderer(menu, [&] {
return menu->Render() | center | size(HEIGHT, GREATER_THAN, 10) | size(WIDTH, GREATER_THAN, 40);
});
ButtonOption button_option{.border = false};
auto controls_container = controls_widget({"OK", "Cancel"}, {ok_callback, screen.ExitLoopClosure()}, &button_option);
auto controls = Renderer(controls_container, [&] {
return controls_container->Render() | hcenter | size(HEIGHT, LESS_THAN, 3) | size(WIDTH, GREATER_THAN, 25);
});
auto global = Container::Vertical({
content,
Renderer([] { return separator(); }),
controls,
});
auto renderer = Renderer(global, [&] {
return tui::centered_interative_multi("New CLI Installer", global);
});
screen.Loop(renderer);
// const auto& temp = utils::exec(fmt::format("echo {0} | sed \"s~{1} [0-9]*[G-M]~~\" | sed \"s~{1} [0-9]*\\.[0-9]*[G-M]~~\" | sed s~{1}$\' -\'~~", partitions, partition));
// spdlog::info("human-info-about-partitions: {}", temp);
// PARTITIONS=$()
// NUMBER_PARTITIONS=$(( NUMBER_PARTITIONS - 1 ))
return true;
}
// BIOS and UEFI
@ -172,16 +94,13 @@ void auto_partition() noexcept {
auto button_option = ButtonOption();
button_option.border = false;
auto button_back = Button("Back", screen.ExitLoopClosure(), &button_option);
auto container = Container::Horizontal({button_back});
auto renderer = Renderer(container, [&] {
return detail::centered_widget(container, "New CLI Installer", detail::multiline_text(utils::make_multiline(disklist)) | size(HEIGHT, GREATER_THAN, 5));
});
/* clang-format on */
auto container = Container::Horizontal({
button_back,
});
auto renderer = Renderer(container, [&] {
return tui::centered_widget(container, "New CLI Installer", multiline_text(utils::make_multiline(disklist)) | size(HEIGHT, GREATER_THAN, 5));
});
screen.Loop(renderer);
}
@ -194,16 +113,13 @@ void show_devices() noexcept {
auto button_option = ButtonOption();
button_option.border = false;
auto button_back = Button("Back", screen.ExitLoopClosure(), &button_option);
auto container = Container::Horizontal({button_back});
auto renderer = Renderer(container, [&] {
return detail::centered_widget(container, "New CLI Installer", detail::multiline_text(utils::make_multiline(lsblk)) | size(HEIGHT, GREATER_THAN, 5));
});
/* clang-format on */
auto container = Container::Horizontal({
button_back,
});
auto renderer = Renderer(container, [&] {
return tui::centered_widget(container, "New CLI Installer", multiline_text(utils::make_multiline(lsblk)) | size(HEIGHT, GREATER_THAN, 5));
});
screen.Loop(renderer);
}
@ -224,7 +140,7 @@ bool select_device() noexcept {
success = true;
std::raise(SIGINT);
};
menu_widget(devices_list, ok_callback, &selected);
detail::menu_widget(devices_list, ok_callback, &selected);
return success;
}
@ -236,7 +152,7 @@ bool select_filesystem() noexcept {
auto* config_instance = Config::instance();
auto& config_data = config_instance->data();
// prep variables
config_data["fs_opts"] = "";
config_data["fs_opts"] = std::vector<std::string>{};
config_data["CHK_NUM"] = 0;
std::vector<std::string> menu_entries = {
@ -262,8 +178,7 @@ bool select_filesystem() noexcept {
const auto& file_sys = lines[0];
if (file_sys == "btrfs") {
config_data["FILESYSTEM"] = "mkfs.btrfs -f";
config_data["CHK_NUM"] = 16;
config_data["fs_opts"] = "autodefrag compress=zlib compress=lzo compress=zstd compress=no compress-force=zlib compress-force=lzo compress-force=zstd discard noacl noatime nodatasum nospace_cache recovery skip_balance space_cache nossd ssd ssd_spread commit=120";
config_data["fs_opts"] = std::vector<std::string>{"autodefrag", "compress=zlib", "compress=lzo", "compress=zstd", "compress=no", "compress-force=zlib", "compress-force=lzo", "compress-force=zstd", "discard", "noacl", "noatime", "nodatasum", "nospace_cache", "recovery", "skip_balance", "space_cache", "nossd", "ssd", "ssd_spread", "commit=120"};
#ifdef NDEVENV
utils::exec("modprobe btrfs");
#endif
@ -273,59 +188,255 @@ bool select_filesystem() noexcept {
config_data["FILESYSTEM"] = "mkfs.ext3 -q";
} else if (file_sys == "ext4") {
config_data["FILESYSTEM"] = "mkfs.ext4 -q";
config_data["CHK_NUM"] = 8;
config_data["fs_opts"] = "data=journal data=writeback dealloc discard noacl noatime nobarrier nodelalloc";
config_data["fs_opts"] = std::vector<std::string>{"data=journal", "data=writeback", "dealloc", "discard", "noacl", "noatime", "nobarrier", "nodelalloc"};
} else if (file_sys == "f2fs") {
config_data["FILESYSTEM"] = "mkfs.f2fs -q";
config_data["fs_opts"] = "data_flush disable_roll_forward disable_ext_identify discard fastboot flush_merge inline_xattr inline_data inline_dentry no_heap noacl nobarrier noextent_cache noinline_data norecovery";
config_data["CHK_NUM"] = 16;
config_data["fs_opts"] = std::vector<std::string>{"data_flush", "disable_roll_forward", "disable_ext_identify", "discard", "fastboot", "flush_merge", "inline_xattr", "inline_data", "inline_dentry", "no_heap", "noacl", "nobarrier", "noextent_cache", "noinline_data", "norecovery"};
#ifdef NDEVENV
utils::exec("modprobe f2fs");
#endif
} else if (file_sys == "jfs") {
config_data["FILESYSTEM"] = "mkfs.jfs -q";
config_data["CHK_NUM"] = 4;
config_data["fs_opts"] = "discard errors=continue errors=panic nointegrity";
config_data["fs_opts"] = std::vector<std::string>{"discard", "errors=continue", "errors=panic", "nointegrity"};
} else if (file_sys == "nilfs2") {
config_data["FILESYSTEM"] = "mkfs.nilfs2 -fq";
config_data["CHK_NUM"] = 7;
config_data["fs_opts"] = "discard nobarrier errors=continue errors=panic order=relaxed order=strict norecovery";
config_data["fs_opts"] = std::vector<std::string>{"discard", "nobarrier", "errors=continue", "errors=panic", "order=relaxed", "order=strict", "norecovery"};
} else if (file_sys == "ntfs") {
config_data["FILESYSTEM"] = "mkfs.ntfs -q";
} else if (file_sys == "reiserfs") {
config_data["FILESYSTEM"] = "mkfs.reiserfs -q";
config_data["CHK_NUM"] = 5;
config_data["fs_opts"] = "acl nolog notail replayonly user_xattr";
config_data["fs_opts"] = std::vector<std::string>{"acl", "nolog", "notail", "replayonly", "user_xattr"};
} else if (file_sys == "vfat") {
config_data["FILESYSTEM"] = "mkfs.vfat -F32";
} else if (file_sys == "xfs") {
config_data["FILESYSTEM"] = "mkfs.xfs -f";
config_data["CHK_NUM"] = 9;
config_data["fs_opts"] = "discard filestreams ikeep largeio noalign nobarrier norecovery noquota wsync";
config_data["fs_opts"] = std::vector<std::string>{"discard", "filestreams", "ikeep", "largeio", "noalign", "nobarrier", "norecovery", "noquota", "wsync"};
}
success = true;
std::raise(SIGINT);
};
menu_widget(menu_entries, ok_callback, &selected);
detail::menu_widget(menu_entries, ok_callback, &selected);
if (!success)
return false;
/* clang-format off */
if (!success) { return false; }
/* clang-format on */
// Warn about formatting!
const auto& file_sys = std::get<std::string>(config_data["FILESYSTEM"]);
const auto& partition = std::get<std::string>(config_data["PARTITION"]);
const auto& content = fmt::format("\nMount {}\n\n! Data on {} will be lost !\n", file_sys, partition);
msgbox_widget(content, size(HEIGHT, LESS_THAN, 15) | size(WIDTH, LESS_THAN, 70));
const auto& do_mount = detail::yesno_widget(content, size(HEIGHT, LESS_THAN, 15) | size(WIDTH, LESS_THAN, 75));
if (do_mount) {
#ifdef NDEVENV
utils::exec(fmt::format("{} {}", file_sys, partition));
#endif
spdlog::info("mount.{} {}", partition, file_sys);
}
return success;
}
bool mount_current_partition() noexcept { return true; }
// This subfunction allows for special mounting options to be applied for relevant fs's.
// Seperate subfunction for neatness.
void mount_opts() noexcept {
auto* config_instance = Config::instance();
auto& config_data = config_instance->data();
const auto& file_sys = std::get<std::string>(config_data["FILESYSTEM"]);
const auto& fs_opts = std::get<std::vector<std::string>>(config_data["fs_opts"]);
const auto& partition = std::get<std::string>(config_data["PARTITION"]);
const auto& format_name = utils::exec(fmt::format("echo {} | rev | cut -d/ -f1 | rev", partition));
const auto& format_device = utils::exec(fmt::format("lsblk -i | tac | sed -r 's/^[^[:alnum:]]+//' | sed -n -e \"/{}/,/disk/p\" | {}", format_name, "awk \'/disk/ {print $1}\'"));
const auto& rotational_queue = (utils::exec(fmt::format("cat /sys/block/{}/queue/rotational", format_device)) == "1");
std::unique_ptr<bool[]> fs_opts_state{new bool[fs_opts.size()]{false}};
for (size_t i = 0; i < fs_opts.size(); ++i) {
const auto& fs_opt = fs_opts[i];
auto& fs_opt_state = fs_opts_state[i];
if (rotational_queue) {
fs_opt_state = ((fs_opt == "autodefrag")
|| (fs_opt == "compress=zlip")
|| (fs_opt == "nossd"));
} else {
fs_opt_state = ((fs_opt == "compress=lzo")
|| (fs_opt == "space_cache")
|| (fs_opt == "commit=120")
|| (fs_opt == "ssd"));
}
/* clang-format off */
if (!fs_opt_state) { fs_opt_state = (fs_opt == "noatime"); }
/* clang-format on */
}
auto flags = Container::Vertical(detail::from_vector_checklist(fs_opts, fs_opts_state.get()));
auto screen = ScreenInteractive::Fullscreen();
auto content = Renderer(flags, [&] {
return flags->Render() | center | size(HEIGHT, GREATER_THAN, 10) | size(WIDTH, GREATER_THAN, 40) | vscroll_indicator;
});
auto& mount_opts_info = std::get<std::string>(config_data["MOUNT_OPTS"]);
auto ok_callback = [&] {
mount_opts_info = detail::from_checklist_string(fs_opts, fs_opts_state.get());
std::raise(SIGINT);
};
ButtonOption button_option{.border = false};
auto controls_container = detail::controls_widget({"OK", "Cancel"}, {ok_callback, screen.ExitLoopClosure()}, &button_option);
auto controls = Renderer(controls_container, [&] {
return controls_container->Render() | hcenter | size(HEIGHT, LESS_THAN, 3) | size(WIDTH, GREATER_THAN, 25);
});
std::string mount_options_body = "\nUse [Space] to de/select the desired mount\noptions and review carefully. Please do not\nselect multiple versions of the same option.\n";
auto global = Container::Vertical({
Renderer([&] { return detail::multiline_text(utils::make_multiline(mount_options_body)); }),
Renderer([] { return separator(); }),
content,
Renderer([] { return separator(); }),
controls,
});
auto renderer = Renderer(global, [&] {
const auto& file_sys_formated = utils::exec(fmt::format("echo {} | sed \"s/.*\\.//g;s/-.*//g\"", file_sys));
const auto& title = fmt::format("New CLI Installer | {}", file_sys_formated);
return detail::centered_interative_multi(title, global);
});
screen.Loop(renderer);
// Now clean up the file
mount_opts_info = utils::exec(fmt::format("echo \"{}\" | sed \'s/ /,/g\'", mount_opts_info));
mount_opts_info = utils::exec(fmt::format("echo \"{}\" | sed \'$s/,$//\'", mount_opts_info));
// If mount options selected, confirm choice
if (!mount_opts_info.empty()) {
auto confirm_text = Container::Vertical({
Renderer([] { return paragraphAlignLeft("Confirm the following mount options:"); }),
Renderer([&] { return text(mount_opts_info) | dim; }),
});
const auto& do_mount = detail::yesno_widget(confirm_text, size(HEIGHT, LESS_THAN, 10) | size(WIDTH, LESS_THAN, 75));
/* clang-format off */
if (!do_mount) { mount_opts_info = ""; }
/* clang-format on */
}
}
bool mount_current_partition() noexcept {
auto* config_instance = Config::instance();
auto& config_data = config_instance->data();
const auto& mountpoint = std::get<std::string>(config_data["MOUNTPOINT"]);
const auto& mount_dev = std::get<std::string>(config_data["MOUNT"]);
#ifdef NDEVENV
// Make the mount directory
fs::path mount_dir(fmt::format("{}{}", mountpoint, mount_dev));
fs::create_directories(mount_dir);
#endif
config_data["MOUNT_OPTS"] = "";
/* clang-format off */
// Get mounting options for appropriate filesystems
const auto& fs_opts = std::get<std::vector<std::string>>(config_data["fs_opts"]);
if (!fs_opts.empty()) { mount_opts(); }
/* clang-format on */
// TODO: use libmount instead.
// see https://github.com/util-linux/util-linux/blob/master/sys-utils/mount.c#L734
#ifdef NDEVENV
const auto& partition = std::get<std::string>(config_data["PARTITION"]);
const auto& mount_opts_info = std::get<std::string>(config_data["MOUNT_OPTS"]);
if (!mount_opts_info.empty()) {
// check_for_error "mount ${PARTITION} $(cat ${MOUNT_OPTS})"
const auto& mount_status = utils::exec(fmt::format("mount -o {} {} {}{}", mount_opts_info, partition, mountpoint, mount_dev));
spdlog::info("{}", mount_status);
} else {
// check_for_error "mount ${PARTITION}"
const auto& mount_status = utils::exec(fmt::format("mount {} {}{}", partition, mountpoint, mount_dev));
spdlog::info("{}", mount_status);
}
#endif
confirm_mount(fmt::format("{}{}", mountpoint, mount_dev));
/*
// Identify if mounted partition is type "crypt" (LUKS on LVM, or LUKS alone)
if [[ $(lsblk -lno TYPE ${PARTITION} | grep "crypt") != "" ]]; then
// cryptname for bootloader configuration either way
LUKS=1
LUKS_NAME=$(echo ${PARTITION} | sed "s~^/dev/mapper/~~g")
# Check if LUKS on LVM (parent = lvm /dev/mapper/...)
cryptparts=$(lsblk -lno NAME,FSTYPE,TYPE | grep "lvm" | grep -i "crypto_luks" | uniq | awk '{print "/dev/mapper/"$1}')
for i in ${cryptparts}; do
if [[ $(lsblk -lno NAME ${i} | grep $LUKS_NAME) != "" ]]; then
LUKS_DEV="$LUKS_DEV cryptdevice=${i}:$LUKS_NAME"
LVM=1
return 0;
fi
done
// Check if LVM on LUKS
cryptparts=$(lsblk -lno NAME,FSTYPE,TYPE | grep " crypt$" | grep -i "LVM2_member" | uniq | awk '{print "/dev/mapper/"$1}')
for i in ${cryptparts}; do
if [[ $(lsblk -lno NAME ${i} | grep $LUKS_NAME) != "" ]]; then
LUKS_DEV="$LUKS_DEV cryptdevice=${i}:$LUKS_NAME"
LVM=1
return 0;
fi
done
// Check if LUKS alone (parent = part /dev/...)
cryptparts=$(lsblk -lno NAME,FSTYPE,TYPE | grep "part" | grep -i "crypto_luks" | uniq | awk '{print "/dev/"$1}')
for i in ${cryptparts}; do
if [[ $(lsblk -lno NAME ${i} | grep $LUKS_NAME) != "" ]]; then
LUKS_UUID=$(lsblk -lno UUID,TYPE,FSTYPE ${i} | grep "part" | grep -i "crypto_luks" | awk '{print $1}')
LUKS_DEV="$LUKS_DEV cryptdevice=UUID=$LUKS_UUID:$LUKS_NAME"
return 0;
fi
done
// If LVM logical volume....
elif [[ $(lsblk -lno TYPE ${PARTITION} | grep "lvm") != "" ]]; then
LVM=1
// First get crypt name (code above would get lv name)
cryptparts=$(lsblk -lno NAME,TYPE,FSTYPE | grep "crypt" | grep -i "lvm2_member" | uniq | awk '{print "/dev/mapper/"$1}')
for i in ${cryptparts}; do
if [[ $(lsblk -lno NAME ${i} | grep $(echo $PARTITION | sed "s~^/dev/mapper/~~g")) != "" ]]; then
LUKS_NAME=$(echo ${i} | sed s~/dev/mapper/~~g)
return 0;
fi
done
// Now get the device (/dev/...) for the crypt name
cryptparts=$(lsblk -lno NAME,FSTYPE,TYPE | grep "part" | grep -i "crypto_luks" | uniq | awk '{print "/dev/"$1}')
for i in ${cryptparts}; do
if [[ $(lsblk -lno NAME ${i} | grep $LUKS_NAME) != "" ]]; then
# Create UUID for comparison
LUKS_UUID=$(lsblk -lno UUID,TYPE,FSTYPE ${i} | grep "part" | grep -i "crypto_luks" | awk '{print $1}')
# Check if not already added as a LUKS DEVICE (i.e. multiple LVs on one crypt). If not, add.
if [[ $(echo $LUKS_DEV | grep $LUKS_UUID) == "" ]]; then
LUKS_DEV="$LUKS_DEV cryptdevice=UUID=$LUKS_UUID:$LUKS_NAME"
LUKS=1
fi
return 0;
fi
done
fi*/
return true;
}
void make_swap() noexcept { }
void mount_partitions() noexcept {
auto* config_instance = Config::instance();
auto& config_data = config_instance->data();
// Warn users that they CAN mount partitions without formatting them!
static constexpr std::string_view content = "\nIMPORTANT: Partitions can be mounted without formatting them\nby selecting the 'Do not format' option listed at the top of\nthe file system menu.\n\nEnsure the correct choices for mounting and formatting\nare made as no warnings will be provided, with the exception of\nthe UEFI boot partition.\n";
msgbox_widget(content, size(HEIGHT, LESS_THAN, 15) | size(WIDTH, LESS_THAN, 70));
detail::msgbox_widget(content, size(HEIGHT, LESS_THAN, 15) | size(WIDTH, LESS_THAN, 70));
// LVM Detection. If detected, activate.
// lvm_detect
@ -381,7 +492,7 @@ void mount_partitions() noexcept {
success = true;
std::raise(SIGINT);
};
menu_widget(partitions, ok_callback, &selected);
detail::menu_widget(partitions, ok_callback, &selected);
if (!success)
return;
}
@ -424,7 +535,7 @@ void mount_partitions() noexcept {
// done
// Identify and create swap, if applicable
// tui::make_swap();
make_swap();
// Now that swap is done we put the legacy partitions back, unless they are already mounted
/*for i in $(zfs_list_datasets "legacy"); do
@ -522,7 +633,7 @@ void create_partitions() noexcept {
};
ButtonOption button_option{.border = false};
auto controls_container = controls_widget({"OK", "Cancel"}, {ok_callback, screen.ExitLoopClosure()}, &button_option);
auto controls_container = detail::controls_widget({"OK", "Cancel"}, {ok_callback, screen.ExitLoopClosure()}, &button_option);
auto controls = Renderer(controls_container, [&] {
return controls_container->Render() | hcenter | size(HEIGHT, LESS_THAN, 3) | size(WIDTH, GREATER_THAN, 25);
@ -535,7 +646,7 @@ void create_partitions() noexcept {
});
auto renderer = Renderer(global, [&] {
return tui::centered_interative_multi("New CLI Installer", global);
return detail::centered_interative_multi("New CLI Installer", global);
});
screen.Loop(renderer);
@ -607,7 +718,7 @@ void prep_menu() noexcept {
};
ButtonOption button_option{.border = false};
auto controls_container = controls_widget({"OK", "Cancel"}, {ok_callback, screen.ExitLoopClosure()}, &button_option);
auto controls_container = detail::controls_widget({"OK", "Cancel"}, {ok_callback, screen.ExitLoopClosure()}, &button_option);
auto controls = Renderer(controls_container, [&] {
return controls_container->Render() | hcenter | size(HEIGHT, LESS_THAN, 3) | size(WIDTH, GREATER_THAN, 25);
@ -620,7 +731,7 @@ void prep_menu() noexcept {
});
auto renderer = Renderer(global, [&] {
return tui::centered_interative_multi("New CLI Installer", global);
return detail::centered_interative_multi("New CLI Installer", global);
});
screen.Loop(renderer);
@ -665,7 +776,10 @@ void init() noexcept {
break;
}
case 3: {
utils::check_mount();
if (!utils::check_mount()) {
screen.ExitLoopClosure();
std::raise(SIGINT);
}
tui::install_custom_menu();
break;
}
@ -680,7 +794,7 @@ void init() noexcept {
};
ButtonOption button_option{.border = false};
auto controls_container = controls_widget({"OK", "Cancel"}, {ok_callback, screen.ExitLoopClosure()}, &button_option);
auto controls_container = detail::controls_widget({"OK", "Cancel"}, {ok_callback, screen.ExitLoopClosure()}, &button_option);
auto controls = Renderer(controls_container, [&] {
return controls_container->Render() | hcenter | size(HEIGHT, LESS_THAN, 3) | size(WIDTH, GREATER_THAN, 25);
@ -693,9 +807,10 @@ void init() noexcept {
});
auto renderer = Renderer(global, [&] {
return tui::centered_interative_multi("New CLI Installer", global);
return detail::centered_interative_multi("New CLI Installer", global);
});
screen.Loop(renderer);
}
} // namespace tui

View File

@ -1,14 +1,7 @@
#ifndef TUI_HPP
#define TUI_HPP
/* clang-format off */
#include <string_view> // for string_view
#include <ftxui/component/component_base.hpp> // for Component
#include <ftxui/dom/elements.hpp> // for Element
/* clang-format on */
namespace tui {
ftxui::Element centered_widget(ftxui::Component& container, const std::string_view& title, const ftxui::Element& widget);
void create_partitions() noexcept;
void init() noexcept;
} // namespace tui

View File

@ -11,6 +11,7 @@
#include <cstdlib> // for exit, WIFEXITED, WIFSIGNALED
#include <filesystem> // for exists, is_directory
#include <iostream> // for basic_istream, cin
#include <ranges> // for std::views::join
#include <regex> // for regex_search, match_results<>::_Base_type
#include <string> // for operator==, string, basic_string, allocator
#include <sys/mount.h> // for mount
@ -127,21 +128,34 @@ bool prompt_char(const char* prompt, const char* color, char* read) noexcept {
}
auto make_multiline(std::string& str, bool reverse, const std::string_view&& delim) noexcept -> std::vector<std::string> {
const auto& view = str
| std::views::split(delim)
| std::views::transform([](auto&& rng) {
const auto& tmp = std::string(&*rng.begin(), static_cast<size_t>(std::ranges::distance(rng)));
return tmp;
});
std::vector<std::string> lines{};
std::size_t start{};
std::size_t end = str.find(delim);
while (end != std::string::npos) {
lines.push_back(str.substr(start, end - start));
start = end + delim.size();
end = str.find(delim, start);
}
lines.push_back(str.substr(start, end - start));
std::ranges::copy(view, std::back_inserter(lines));
if (reverse) {
std::reverse(lines.begin(), lines.end());
std::ranges::reverse(lines);
}
return lines;
}
auto make_multiline(std::vector<std::string>& multiline, bool reverse, const std::string_view&& delim) noexcept -> std::string {
std::string res{};
// for (const char c : multiline | std::views::join) res += c;
for (const auto& line : multiline) {
res += line;
res += delim.data();
}
return lines;
if (reverse) {
std::ranges::reverse(res.begin(), res.end());
}
return res;
}
// install a pkg in the live session if not installed

View File

@ -14,10 +14,12 @@ void print_banner() noexcept;
bool prompt_char(const char* prompt, const char* color = RESET, char* read = nullptr) noexcept;
void clear_screen() noexcept;
[[nodiscard]] auto make_multiline(std::string& str, bool reverse = false, const std::string_view&& delim = "\n") noexcept -> std::vector<std::string>;
[[nodiscard]] auto make_multiline(std::vector<std::string>& multiline, bool reverse = false, const std::string_view&& delim = "\n") noexcept -> std::string;
void secure_wipe() noexcept;
bool check_mount() noexcept;
std::string list_mounted() noexcept;
std::string list_containing_crypt() noexcept;
[[nodiscard]] bool check_mount() noexcept;
[[nodiscard]] std::string list_mounted() noexcept;
[[nodiscard]] std::string list_containing_crypt() noexcept;
[[nodiscard]] std::string list_non_crypt() noexcept;
void umount_partitions() noexcept;
void find_partitions() noexcept;

248
src/widgets.cpp Normal file
View File

@ -0,0 +1,248 @@
#include "widgets.hpp"
#include "config.hpp"
#include "utils.hpp"
/* clang-format off */
#include <csignal> // for raise
#include <algorithm> // for transform
#include <string> // for basic_string
#include <ftxui/component/component.hpp> // for Renderer, Button
#include <ftxui/component/component_options.hpp> // for ButtonOption
#include <ftxui/component/screen_interactive.hpp> // for Component, ScreenI...
/* clang-format on */
using namespace ftxui;
namespace tui::detail {
Element centered_widget(Component& container, const std::string_view& title, const Element& widget) noexcept {
return vbox({
// -------- Title --------------
text(title.data()) | bold,
filler(),
// -------- Center Menu --------------
hbox({
filler(),
border(vbox({
widget,
separator(),
container->Render() | hcenter | size(HEIGHT, LESS_THAN, 3) | size(WIDTH, GREATER_THAN, 25),
})),
filler(),
}) | center,
filler(),
});
}
Element centered_widget_nocontrols(const std::string_view& title, const Element& widget) noexcept {
return vbox({
// -------- Title --------------
text(title.data()) | bold,
filler(),
// -------- Center Menu --------------
hbox({
filler(),
border(vbox({widget})),
filler(),
}) | center,
filler(),
});
}
Component controls_widget(const std::array<std::string_view, 2>&& titles, const std::array<std::function<void()>, 2>&& callbacks, ButtonOption* button_option) noexcept {
/* clang-format off */
auto button_ok = Button(titles[0].data(), callbacks[0], button_option);
auto button_quit = Button(titles[1].data(), callbacks[1], button_option);
/* clang-format on */
auto container = Container::Horizontal({
button_ok,
Renderer([] { return filler() | size(WIDTH, GREATER_THAN, 3); }),
button_quit,
});
return container;
}
Element centered_interative_multi(const std::string_view& title, Component& widgets) noexcept {
return vbox({
// -------- Title --------------
text(title.data()) | bold,
filler(),
// -------- Center Menu --------------
hbox({
filler(),
border(vbox({
widgets->Render(),
})),
filler(),
}) | center,
filler(),
});
}
Element multiline_text(const std::vector<std::string>& lines) noexcept {
Elements multiline;
std::transform(lines.cbegin(), lines.cend(), std::back_inserter(multiline),
[=](const std::string& line) -> Element { return text(line); });
return vbox(std::move(multiline)) | frame;
}
Components from_vector_checklist(const std::vector<std::string>& opts, bool* opts_state) noexcept {
Components components;
for (size_t i = 0; i < opts.size(); ++i) {
auto component = Checkbox(&opts[i], &opts_state[i]);
components.emplace_back(component);
}
return components;
}
std::string from_checklist_string(const std::vector<std::string>& opts, bool* opts_state) noexcept {
std::string res{};
for (size_t i = 0; i < opts.size(); ++i) {
if (opts_state[i]) {
res += opts[i] + " ";
}
}
/* clang-format off */
if (res.ends_with(" ")) { res.pop_back(); }
/* clang-format on */
return res;
}
std::vector<std::string> from_checklist_vector(const std::vector<std::string>& opts, bool* opts_state) noexcept {
std::vector<std::string> res{};
for (size_t i = 0; i < opts.size(); ++i) {
if (opts_state[i]) {
res.push_back(opts[i]);
}
}
return res;
}
void msgbox_widget(const std::string_view& content, Decorator boxsize) noexcept {
auto screen = ScreenInteractive::Fullscreen();
/* clang-format off */
auto button_option = ButtonOption();
button_option.border = false;
auto button_back = Button("OK", screen.ExitLoopClosure(), &button_option);
/* clang-format on */
auto container = Container::Horizontal({
button_back,
});
std::string tmp{content.data()};
auto renderer = Renderer(container, [&] {
return centered_widget(container, "New CLI Installer", multiline_text(utils::make_multiline(tmp)) | hcenter | boxsize);
});
screen.Loop(renderer);
}
void infobox_widget(const std::string_view& content, Decorator boxsize) noexcept {
auto screen = Screen::Create(
Dimension::Full(), // Width
Dimension::Full() // Height
);
std::string tmp{content.data()};
auto element = centered_widget_nocontrols("New CLI Installer", multiline_text(utils::make_multiline(tmp)) | vcenter | boxsize);
Render(screen, element);
screen.Print();
}
bool yesno_widget(const std::string_view& content, Decorator boxsize) noexcept {
auto screen = ScreenInteractive::Fullscreen();
bool success{};
auto ok_callback = [&] {
success = true;
std::raise(SIGINT);
};
ButtonOption button_option{.border = false};
auto controls_container = controls_widget({"OK", "Cancel"}, {ok_callback, screen.ExitLoopClosure()}, &button_option);
auto controls = Renderer(controls_container, [&] {
return controls_container->Render() | hcenter | size(HEIGHT, LESS_THAN, 3) | size(WIDTH, GREATER_THAN, 25);
});
auto container = Container::Horizontal({
controls,
});
auto renderer = Renderer(container, [&] {
return centered_widget(container, "New CLI Installer", text(content.data()) | hcenter | boxsize);
});
screen.Loop(renderer);
return success;
}
bool yesno_widget(ftxui::Component& container, Decorator boxsize) noexcept {
auto screen = ScreenInteractive::Fullscreen();
auto content = Renderer(container, [&] {
return container->Render() | hcenter | boxsize;
});
bool success{};
auto ok_callback = [&] {
success = true;
std::raise(SIGINT);
};
ButtonOption button_option{.border = false};
auto controls_container = controls_widget({"OK", "Cancel"}, {ok_callback, screen.ExitLoopClosure()}, &button_option);
auto controls = Renderer(controls_container, [&] {
return controls_container->Render() | hcenter | size(HEIGHT, LESS_THAN, 3) | size(WIDTH, GREATER_THAN, 25);
});
auto global = Container::Vertical({
content,
Renderer([] { return separator(); }),
controls,
});
auto renderer = Renderer(global, [&] {
return centered_interative_multi("New CLI Installer", global);
});
screen.Loop(renderer);
return success;
}
void menu_widget(const std::vector<std::string>& entries, const std::function<void()>&& ok_callback, std::int32_t* selected) noexcept {
auto screen = ScreenInteractive::Fullscreen();
auto menu = Menu(&entries, selected);
auto content = Renderer(menu, [&] {
return menu->Render() | center | size(HEIGHT, GREATER_THAN, 10) | size(WIDTH, GREATER_THAN, 40);
});
ButtonOption button_option{.border = false};
auto controls_container = controls_widget({"OK", "Cancel"}, {ok_callback, screen.ExitLoopClosure()}, &button_option);
auto controls = Renderer(controls_container, [&] {
return controls_container->Render() | hcenter | size(HEIGHT, LESS_THAN, 3) | size(WIDTH, GREATER_THAN, 25);
});
auto global = Container::Vertical({
content,
Renderer([] { return separator(); }),
controls,
});
auto renderer = Renderer(global, [&] {
return centered_interative_multi("New CLI Installer", global);
});
screen.Loop(renderer);
}
} // namespace tui::detail

28
src/widgets.hpp Normal file
View File

@ -0,0 +1,28 @@
#ifndef WIDGETS_HPP
#define WIDGETS_HPP
/* clang-format off */
#include <string_view> // for string_view
#include <ftxui/component/component_base.hpp> // for Component
#include <ftxui/component/component_options.hpp> // for ButtonOption
#include <ftxui/dom/elements.hpp> // for Element, operator|, size
/* clang-format on */
namespace tui {
namespace detail {
auto centered_widget(ftxui::Component& container, const std::string_view& title, const ftxui::Element& widget) noexcept -> ftxui::Element;
auto controls_widget(const std::array<std::string_view, 2>&& titles, const std::array<std::function<void()>, 2>&& callbacks, ftxui::ButtonOption* button_option) noexcept -> ftxui::Component;
auto centered_interative_multi(const std::string_view& title, ftxui::Component& widgets) noexcept -> ftxui::Element;
auto multiline_text(const std::vector<std::string>& lines) noexcept -> ftxui::Element;
auto from_vector_checklist(const std::vector<std::string>& opts, bool* opts_state) noexcept -> ftxui::Components;
auto from_checklist_string(const std::vector<std::string>& opts, bool* opts_state) noexcept -> std::string;
auto from_checklist_vector(const std::vector<std::string>& opts, bool* opts_state) noexcept -> std::vector<std::string>;
void msgbox_widget(const std::string_view& content, ftxui::Decorator boxsize = size(ftxui::HEIGHT, ftxui::GREATER_THAN, 5)) noexcept;
void infobox_widget(const std::string_view& content, ftxui::Decorator boxsize = size(ftxui::HEIGHT, ftxui::GREATER_THAN, 5)) noexcept;
bool yesno_widget(const std::string_view& content, ftxui::Decorator boxsize = size(ftxui::HEIGHT, ftxui::GREATER_THAN, 5)) noexcept;
bool yesno_widget(ftxui::Component& container, ftxui::Decorator boxsize = size(ftxui::HEIGHT, ftxui::GREATER_THAN, 5)) noexcept;
void menu_widget(const std::vector<std::string>& entries, const std::function<void()>&& ok_callback, std::int32_t* selected) noexcept;
} // namespace detail
} // namespace tui
#endif // WIDGETS_HPP

View File

@ -1,6 +1,6 @@
[wrap-git]
url = https://github.com/libcpr/cpr.git
revision = f229f82c1b38febebbf4e9958cebcd64caa32947
revision = beb9e98806bb84bcc130a2cebfbcbbc6ce62b335
patch_directory = cpr

View File

@ -1,6 +1,6 @@
[wrap-git]
url = https://github.com/fmtlib/fmt.git
revision = c472a2781852778cc3f06521a3384a2c5c5822d5
revision = 3a951a66cb0fb53ff5a9d5ce9c77e05ef9d382ce
patch_directory = fmt

View File

@ -1,6 +1,6 @@
[wrap-git]
url = https://github.com/arthursonzogni/ftxui.git
revision = cecd54df42dd66fdf8386ed461e16b725bffc827
revision = 602392c43d8bc2272c6548ae75b8802ab7ad0b2a
patch_directory = ftxui

View File

@ -1,5 +1,5 @@
project('cpr', 'cpp',
version: '1.7.0',
version: '1.7.2',
license : 'MIT',
default_options: ['cpp_std=c++11']
)

View File

@ -29,6 +29,7 @@ ftxui_screen_lib = static_library('ftxui_screen',
ftxui_dom_lib = static_library('ftxui_dom',
sources : [
'include/ftxui/dom/elements.hpp',
'include/ftxui/dom/flexbox_config.hpp',
'include/ftxui/dom/node.hpp',
'include/ftxui/dom/requirement.hpp',
'include/ftxui/dom/take_any_args.hpp',
@ -43,12 +44,15 @@ ftxui_dom_lib = static_library('ftxui_dom',
'src/ftxui/dom/dbox.cpp',
'src/ftxui/dom/dim.cpp',
'src/ftxui/dom/flex.cpp',
'src/ftxui/dom/flexbox.cpp',
'src/ftxui/dom/flexbox_config.cpp',
'src/ftxui/dom/flexbox_helper.cpp',
'src/ftxui/dom/flexbox_helper.hpp',
'src/ftxui/dom/frame.cpp',
'src/ftxui/dom/gauge.cpp',
'src/ftxui/dom/graph.cpp',
'src/ftxui/dom/gridbox.cpp',
'src/ftxui/dom/hbox.cpp',
'src/ftxui/dom/hflow.cpp',
'src/ftxui/dom/inverted.cpp',
'src/ftxui/dom/node.cpp',
'src/ftxui/dom/node_decorator.hpp',