🚧 update

This commit is contained in:
Vladislav Nepogodin 2021-09-18 01:55:33 +04:00
parent 4c2e31ae6b
commit d1c92376c4
No known key found for this signature in database
GPG Key ID: B62C3D10C54D5DA9
10 changed files with 621 additions and 229 deletions

View File

@ -1,5 +1,7 @@
cmake_minimum_required(VERSION 3.14)
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
##
## PROJECT
## name and version
@ -15,6 +17,9 @@ message(STATUS "BUILD: ${CMAKE_BUILD_TYPE}")
##
## INCLUDE
##
include(CompilerWarnings)
include(EnableCcache)
include(ClangTidy)
include(FetchContent)
find_package(PkgConfig REQUIRED)
@ -22,13 +27,16 @@ pkg_check_modules(GTK3 REQUIRED gtkmm-3.0)
FetchContent_Declare(fmt
GIT_REPOSITORY "https://github.com/fmtlib/fmt.git"
GIT_TAG "d9fd695ac737f84f7de2d0a2aa346b25efb9afbf"
GIT_TAG "2742611cad4aee6b1a5638bd1ebf132908f4a3d9"
)
FetchContent_MakeAvailable(fmt)
##
## CONFIGURATION
##
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -flto")
if(UNIX AND CMAKE_GENERATOR STREQUAL "Ninja")
if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
set(CMAKE_CXX_FLAGS "-fcolor-diagnostics ${CMAKE_CXX_FLAGS}")
@ -40,13 +48,19 @@ if(UNIX AND CMAKE_GENERATOR STREQUAL "Ninja")
endif()
endif()
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -flto")
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -fwhole-program")
endif()
add_library(project_warnings INTERFACE)
set_project_warnings(project_warnings)
##
## Target
##
add_executable(${PROJECT_NAME}
src/main.cc
src/hello.cpp src/hello.hpp
src/main.cpp
)
target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_17)

15
cmake/ClangTidy.cmake Normal file
View File

@ -0,0 +1,15 @@
option(ENABLE_TIDY "Enable clang-tidy [default: OFF]" OFF)
if(ENABLE_TIDY)
find_program(CLANG_TIDY_EXE
NAMES clang-tidy-9 clang-tidy-8 clang-tidy-7 clang-tidy
DOC "Path to clang-tidy executable")
if(NOT CLANG_TIDY_EXE)
message(STATUS "[clang-tidy] Not found.")
else()
message(STATUS "[clang-tidy] found: ${CLANG_TIDY_EXE}")
set(CMAKE_CXX_CLANG_TIDY "${CLANG_TIDY_EXE}")
endif()
else()
message(STATUS "[clang-tidy] Disabled.")
endif()

View File

@ -0,0 +1,76 @@
function(set_project_warnings project_name)
option(WARNINGS_AS_ERROR "Treat compiler warnings as error" ON)
set(MSVC_WARNINGS
/W4 # Base
/w14242 # Conversion
/w14254 # Operator convers.
/w14263 # Func member doesn't override
/w14265 # class has vfuncs, but destructor is not
/w14287 # unsigned/negative constant mismatch
/we4289 # nonstandard extension used: loop control var
/w14296 # expression is always 'boolean_value'
/w14311 # pointer trunc from one tipe to another
/w14545 # expression before comma evaluates to a function which missign an argument list
/w14546 # function call before comma missing argument list
/w14547 # operator before comma has no effect; expected operator with side-effect
/w14549 # operator before comma has no effect; did you intend operator?
/w14555 # expresion has no effect; expected expression with side-effect
/w14619 # pragma warning
/w14640 # Enable warning on thread; static member
/w14826 # Conversion from one tipe to another is sign-extended cause unexpected runtime behavior.
/w14928 # illegal copy-initialization; more than user-defined.
/X
/constexpr
)
set(CLANG_WARNINGS
-Wall
-Wextra # standard
-Wshadow
-Wnon-virtual-dtor
-Wold-style-cast # c-style cast
-Wcast-align
-Wunused
-Woverloaded-virtual
-Wpedantic # non-standard C++
-Wconversion # type conversion that may lose data
-Wsign-conversion
-Wnull-dereference
-Wdouble-promotion # float to double
-Wformat=2
)
if(WARNINGS_AS_ERRORS)
set(CLANG_WARNINGS ${CLANG_WARNINGS} -Werror)
set(MSVC_WARNINGS ${MSVC_WARNINGS} /WX)
endif()
set(GCC_WARNINGS
${CLANG_WARNINGS}
-Wmisleading-indentation
-Wduplicated-cond
-Wduplicated-branches
-Wlogical-op
-Wuseless-cast
)
if(MSVC)
set(PROJECT_WARNINGS ${MSVC_WARNINGS})
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
set(PROJECT_WARNINGS ${CLANG_WARNINGS})
else()
set(PROJECT_WARNINGS ${GCC_WARNINGS})
endif()
target_compile_options(${project_name} INTERFACE ${PROJECT_WARNINGS})
endfunction()

13
cmake/EnableCcache.cmake Normal file
View File

@ -0,0 +1,13 @@
# Setup ccache.
#
# The ccache is auto-enabled if the tool is found.
# To disable set -DCCACHE=OFF option.
if(NOT DEFINED CMAKE_CXX_COMPILER_LAUNCHER)
find_program(CCACHE ccache DOC "ccache tool path; set to OFF to disable")
if(CCACHE)
set(CMAKE_CXX_COMPILER_LAUNCHER ${CCACHE})
message(STATUS "[ccache] Enabled: ${CCACHE}")
else()
message(STATUS "[ccache] Disabled.")
endif()
endif()

42
compile_flags.txt Normal file
View File

@ -0,0 +1,42 @@
-std=gnu++17
-DFMT_LOCALE
-I/usr/include/gtkmm-3.0
-I/usr/lib/gtkmm-3.0/include
-I/usr/include/giomm-2.4
-I/usr/lib/giomm-2.4/include
-I/usr/include/glib-2.0
-I/usr/lib/glib-2.0/include
-I/usr/include/libmount
-I/usr/include/blkid
-I/usr/include/glibmm-2.4
-I/usr/lib/glibmm-2.4/include
-I/usr/include/sigc++-2.0
-I/usr/lib/sigc++-2.0/include
-I/usr/include/gtk-3.0
-I/usr/include/pango-1.0
-I/usr/include/harfbuzz
-I/usr/include/freetype2
-I/usr/include/libpng16
-I/usr/include/fribidi
-I/usr/include/cairo
-I/usr/include/lzo
-I/usr/include/pixman-1
-I/usr/include/gdk-pixbuf-2.0
-I/usr/include/gio-unix-2.0
-I/usr/include/cloudproviders
-I/usr/include/atk-1.0
-I/usr/include/at-spi2-atk/2.0
-I/usr/include/dbus-1.0
-I/usr/lib/dbus-1.0/include
-I/usr/include/at-spi-2.0
-I/usr/include/cairomm-1.0
-I/usr/lib/cairomm-1.0/include
-I/usr/include/pangomm-1.4
-I/usr/lib/pangomm-1.4/include
-I/usr/include/atkmm-1.6
-I/usr/lib/atkmm-1.6/include
-I/usr/include/gtk-3.0/unix-print
-I/usr/include/gdkmm-3.0
-I/usr/lib/gdkmm-3.0/include
-Isrc
-xc++

362
src/hello.cpp Normal file
View File

@ -0,0 +1,362 @@
#include "hello.hpp"
#include "helper.hpp"
#include <libintl.h>
#include <filesystem>
#include <fstream>
#include <iostream>
#include <iterator>
#include <unordered_map>
#include <fmt/core.h>
namespace fs = std::filesystem;
namespace {
Hello* g_refHello;
std::string fix_path(std::string&& path) noexcept {
if (path[0] != '~') {
return path;
}
replace_all(path, "~", Glib::get_home_dir().c_str());
return path;
}
nlohmann::json read_json(const std::string_view& path) {
// read a JSON file
std::ifstream i(fix_path(path.data()));
nlohmann::json j;
i >> j;
return j;
}
void write_json(const std::string_view& path, const nlohmann::json& content) {
// write data to JSON file
std::ofstream o(fix_path(path.data()));
o << content << '\n';
}
// Read information from the lsb-release file.
//
// @Returns args from lsb-release file
std::array<std::string, 2> get_lsb_infos() {
std::unordered_map<std::string, std::string> lsb{};
try {
std::ifstream lsb_release("/etc/lsb-release");
std::string line;
while (std::getline(lsb_release, line)) {
if (line.find('=') != std::string::npos) {
auto var = tokenize(line, "=");
remove_all(var.first, "DISTRIB_");
remove_all(var.second, "\"");
lsb[var.first] = var.second;
}
}
} catch (const std::exception& e) {
std::cerr << e.what() << '\n';
return {"not CachyOS", "0.0"};
}
return {lsb["ID"], lsb["RELEASE"]};
}
} // namespace
Hello::Hello(int argc, char** argv) {
set_title("CachyOS Hello");
set_border_width(6);
if (argc > 1 && (strncmp(argv[1], "--dev", 5) == 0)) {
m_dev = true;
}
g_refHello = this;
auto screen = Gdk::Screen::get_default();
// Load preferences
if (m_dev) {
m_preferences = read_json("data/preferences.json");
m_preferences["data_path"] = "data/";
m_preferences["desktop_path"] = fmt::format("{}/{}.desktop", fs::current_path().string(), m_app);
m_preferences["locale_path"] = "locale/";
m_preferences["ui_path"] = fmt::format("ui/{}.glade", m_app);
m_preferences["style_path"] = "ui/style.css";
} else {
m_preferences = read_json(fmt::format("/usr/share/{}/data/preferences.json", m_app));
}
// Get saved infos
const auto& save_path = fix_path(m_preferences["save_path"]);
m_save = (!fs::exists(save_path)) ? nlohmann::json({{"locale", ""}}) : read_json(save_path);
// Import Css
auto provider = Gtk::CssProvider::create();
provider->load_from_path(m_preferences["style_path"]);
Gtk::StyleContext::add_provider_for_screen(screen, provider, GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
// Init window
m_builder = Gtk::Builder::create_from_file(m_preferences["ui_path"]);
gtk_builder_add_callback_symbol(m_builder->gobj(), "on_languages_changed", G_CALLBACK(on_languages_changed));
gtk_builder_add_callback_symbol(m_builder->gobj(), "on_action_clicked", G_CALLBACK(on_action_clicked));
gtk_builder_add_callback_symbol(m_builder->gobj(), "on_btn_clicked", G_CALLBACK(on_btn_clicked));
gtk_builder_add_callback_symbol(m_builder->gobj(), "on_link_clicked", G_CALLBACK(on_link_clicked));
gtk_builder_add_callback_symbol(m_builder->gobj(), "on_delete_window", G_CALLBACK(on_delete_window));
gtk_builder_connect_signals(m_builder->gobj(), nullptr);
Gtk::Window* ref_window;
m_builder->get_widget("window", ref_window);
gobject_ = reinterpret_cast<GObject*>(ref_window->gobj());
// Subtitle of headerbar
Gtk::HeaderBar* header;
m_builder->get_widget("headerbar", header);
const auto& lsb_info = get_lsb_infos();
header->set_subtitle(lsb_info[0] + " " + lsb_info[1]);
// Load images
if (fs::is_regular_file(m_preferences["logo_path"])) {
const auto& logo = Gdk::Pixbuf::create_from_file(m_preferences["logo_path"]);
set_icon(logo);
Gtk::Image* image;
m_builder->get_widget("distriblogo", image);
image->set(logo);
Gtk::AboutDialog* dialog;
m_builder->get_widget("aboutdialog", dialog);
dialog->set_logo(logo);
}
Gtk::Box* social_box;
m_builder->get_widget("social", social_box);
for (const auto& btn : social_box->get_children()) {
const auto& name = btn->get_name();
const auto& icon_path = fmt::format("{}img/{}.png", m_preferences["data_path"], name.c_str());
Gtk::Image* image;
m_builder->get_widget(name, image);
image->set(icon_path);
}
Gtk::Grid* homepage_grid;
m_builder->get_widget("homepage", homepage_grid);
for (const auto& widget : homepage_grid->get_children()) {
if (!G_TYPE_CHECK_INSTANCE_TYPE(widget->gobj(), GTK_TYPE_BUTTON)) {
continue;
}
const auto& casted_widget = Glib::wrap(GTK_BUTTON(widget->gobj()));
if (gtk_button_get_image_position(casted_widget->gobj()) != GtkPositionType::GTK_POS_RIGHT) {
continue;
}
const auto& image_path = fmt::format("{}img/external-link.png", m_preferences["data_path"]);
Gtk::Image image;
image.set(image_path);
image.set_margin_start(2);
casted_widget->set_image(image);
}
// Create pages
m_pages = fmt::format("{}pages/{}", m_preferences["data_path"], m_preferences["default_locale"]);
for (const auto& page : fs::directory_iterator(m_pages)) {
auto* scrolled_window = gtk_scrolled_window_new(nullptr, nullptr);
auto* viewport = gtk_viewport_new(nullptr, nullptr);
gtk_container_set_border_width(GTK_CONTAINER(viewport), 10);
auto* label = gtk_label_new(nullptr);
gtk_label_set_line_wrap(GTK_LABEL(label), true);
auto* image = gtk_image_new_from_icon_name("go-previous", GTK_ICON_SIZE_BUTTON);
auto* backBtn = gtk_button_new();
gtk_button_set_image(GTK_BUTTON(backBtn), image);
gtk_widget_set_name(backBtn, "home");
g_signal_connect(backBtn, "clicked", G_CALLBACK(&on_btn_clicked), nullptr);
auto* grid = GTK_GRID(gtk_grid_new());
gtk_grid_attach(grid, backBtn, 0, 1, 1, 1);
gtk_grid_attach(grid, label, 1, 2, 1, 1);
gtk_container_add(GTK_CONTAINER(viewport), GTK_WIDGET(grid));
gtk_container_add(GTK_CONTAINER(scrolled_window), GTK_WIDGET(viewport));
gtk_widget_show_all(scrolled_window);
Glib::RefPtr<Glib::Object> stack = m_builder->get_object("stack");
const auto& child_name = page.path().filename().string() + "page";
gtk_stack_add_named(GTK_STACK(stack->gobj()), scrolled_window, child_name.c_str());
}
// Init translation
const std::string& locale_path = m_preferences["locale_path"];
bindtextdomain(m_app, locale_path.c_str());
textdomain(m_app);
Gtk::ComboBoxText* languages;
m_builder->get_widget("languages", languages);
languages->set_active_id(get_best_locale());
// Set autostart switcher state
m_autostart = fs::is_regular_file(fix_path(m_preferences["autostart_path"]));
Gtk::Switch* autostart_switch;
m_builder->get_widget("autostart", autostart_switch);
autostart_switch->set_active(m_autostart);
}
/// Returns the best locale, based on user's preferences.
auto Hello::get_best_locale() const noexcept -> std::string {
const auto& binary_path = fmt::format("{}{}{}.mo", m_preferences["locale_path"], "{}/LC_MESSAGES/", m_app);
const auto& saved_locale = fmt::vformat(binary_path, fmt::make_format_args(m_save["locale"]));
if (fs::is_regular_file(saved_locale)) {
return m_save["locale"];
} else if (m_save["locale"] == m_preferences["default_locale"]) {
return m_preferences["default_locale"];
}
const auto& locale_name = std::locale("").name();
std::string sys_locale = locale_name.substr(0, locale_name.find('.'));
const auto& user_locale = fmt::vformat(binary_path, fmt::make_format_args(sys_locale));
const auto& two_letters = sys_locale.substr(0, 2);
// If user's locale is supported
if (fs::is_regular_file(user_locale)) {
if (sys_locale.find('_') != std::string::npos) {
replace_all(sys_locale, "_", "-");
}
return sys_locale;
}
// If two first letters of user's locale is supported (ex: en_US -> en)
else if (fs::is_regular_file(fmt::vformat(binary_path, fmt::make_format_args(two_letters)))) {
return two_letters;
}
return m_preferences["default_locale"];
}
/// Sets locale of ui and pages.
void Hello::set_locale(const std::string_view& use_locale) noexcept {
fmt::print(
"┌{0:─^{2}}┐\n"
"│{1: ^{2}}│\n"
"└{0:─^{2}}┘\n",
"", fmt::format("Locale changed to {}", use_locale), 40);
bind_textdomain_codeset(m_app, use_locale.data());
m_save["locale"] = use_locale;
// Real-time locale changing
/* clang-format off */
nlohmann::json elts = {
{"comments", {"aboutdialog"}},
{"label", {
"autostartlabel",
"development",
"discover",
"donate",
"firstcategory",
"forum",
"install",
"installlabel",
"involved",
"mailling",
"readme",
"release",
"secondcategory",
"thirdcategory",
"welcomelabel",
"welcometitle",
"wiki"}
},
{"tooltip_text", {
"about",
"development",
"discover",
"donate",
"forum",
"mailling",
"wiki"}
}};
/* clang-format on */
for (const auto& method : elts.items()) {
if (!m_default_texts.contains(method.key())) {
m_default_texts[method.key()] = {};
}
for (const auto& elt : elts[method.key()].items()) {
const std::string& elt_value = elt.value();
if (!m_default_texts[method.key()].contains(elt_value)) {
Gtk::Widget* item;
m_builder->get_widget(elt_value, item);
gchar* item_buf;
g_object_get(G_OBJECT(item->gobj()), method.key().c_str(), &item_buf, nullptr);
m_default_texts[method.key()][elt_value] = item_buf;
g_free(item_buf);
}
}
}
// Change content of pages
for (const auto& page : fs::directory_iterator(m_pages)) {
Gtk::Stack* stack;
m_builder->get_widget("stack", stack);
const auto& child = stack->get_child_by_name((page.path().filename().string() + "page").c_str());
if (child == nullptr) {
fmt::print(stderr, "child not found\n");
continue;
}
const auto& first_child = reinterpret_cast<Gtk::Container*>(child)->get_children();
const auto& second_child = reinterpret_cast<Gtk::Container*>(first_child[0])->get_children();
const auto& third_child = reinterpret_cast<Gtk::Container*>(second_child[0])->get_children();
const auto& label = reinterpret_cast<Gtk::Label*>(third_child[0]);
label->set_markup(get_page(page.path().filename().string()));
}
}
auto Hello::get_page(const std::string& name) const noexcept -> std::string {
auto filename = fmt::format("{}pages/{}/{}", m_preferences["data_path"], m_save["locale"], name);
if (!fs::is_regular_file(filename)) {
filename = fmt::format("{}pages/{}/{}", m_preferences["data_path"], m_preferences["default_locale"], name);
}
return read_whole_file(filename);
}
// Handlers
void Hello::on_languages_changed(GtkComboBox* combobox) noexcept {
const auto& active_id = gtk_combo_box_get_active_id(combobox);
g_refHello->set_locale(active_id);
}
void Hello::on_action_clicked(GtkWidget* widget) noexcept {
const auto& name = gtk_widget_get_name(widget);
if (strncmp(name, "install", 7) == 0) {
fmt::print("install\n");
return;
} else if (strncmp(name, "autostart", 9) == 0) {
const auto& action = Glib::wrap(GTK_SWITCH(widget));
//set_autostart(action->get_active());
fmt::print("autostart\n");
return;
}
Gtk::AboutDialog* dialog;
g_refHello->m_builder->get_widget("aboutdialog", dialog);
dialog->set_decorated(false);
dialog->run();
dialog->hide();
}
void Hello::on_btn_clicked(GtkWidget* widget) noexcept {
const auto& name = gtk_widget_get_name(widget);
Gtk::Stack* stack;
g_refHello->m_builder->get_widget("stack", stack);
stack->set_visible_child(fmt::format("{}page", name).c_str());
}
void Hello::on_link_clicked(GtkWidget* widget) noexcept {
const auto& name = gtk_widget_get_name(widget);
const std::string uri = g_refHello->m_preferences["urls"][name];
gtk_show_uri_on_window(nullptr, uri.c_str(), GDK_CURRENT_TIME, nullptr);
}
void Hello::on_delete_window(GtkWidget* /*widget*/) noexcept {
write_json(g_refHello->m_preferences["save_path"].get<std::string>(), g_refHello->m_save);
const auto& application = g_refHello->get_application();
application->quit();
}

35
src/hello.hpp Normal file
View File

@ -0,0 +1,35 @@
#ifndef HELLO_HPP_
#define HELLO_HPP_
#include <gtkmm.h>
#include <json.hpp>
class Hello final : public Gtk::Window {
public:
Hello(int argc, char** argv);
protected:
// Handlers
static void on_languages_changed(GtkComboBox* combobox) noexcept;
static void on_action_clicked(GtkWidget* widget) noexcept;
static void on_btn_clicked(GtkWidget* widget) noexcept;
static void on_link_clicked(GtkWidget* widget) noexcept;
static void on_delete_window(GtkWidget* /*widget*/) noexcept;
private:
static constexpr auto m_app = "cachyos-hello";
bool m_dev{};
bool m_autostart{};
std::string m_pages;
Glib::RefPtr<Gtk::Builder> m_builder;
nlohmann::json m_preferences;
nlohmann::json m_save;
nlohmann::json m_default_texts;
auto get_best_locale() const noexcept -> std::string;
void set_locale(const std::string_view& use_locale) noexcept;
auto get_page(const std::string& name) const noexcept -> std::string;
};
#endif // HELLO_HPP_

51
src/helper.hpp Normal file
View File

@ -0,0 +1,51 @@
// Helper macroses
#ifndef HELPER_HPP_
#define HELPER_HPP_
#include <fstream>
#include <string_view>
#include <utility>
#include <gtk/gtk.h>
inline std::pair<std::string, std::string> tokenize(std::string& str, const std::string_view& delim) {
int start{};
int end = str.find(delim.data());
std::string key;
while (end != -1) {
key = str.substr(start, end - start);
start = end + delim.length();
end = str.find(delim.data(), start);
}
return {key, str.substr(start, end - start)};
}
inline std::size_t replace_all(std::string& inout, const std::string_view& what, const std::string_view& with) {
std::size_t count{};
std::size_t pos{};
while (inout.npos != (pos = inout.find(what.data(), pos, what.length()))) {
inout.replace(pos, what.length(), with.data(), with.length());
pos += with.length(), ++count;
}
return count;
}
inline std::size_t remove_all(std::string& inout, const std::string_view& what) {
return replace_all(inout, what, "");
}
auto read_whole_file(const std::string_view& path) noexcept -> std::string {
static constexpr auto read_size = 4096;
std::ifstream stream{path.data()};
stream.exceptions(std::ios_base::badbit);
std::string file{};
std::string buf(read_size, '\0');
while (stream.read(&buf[0], read_size)) {
file.append(buf, 0, stream.gcount());
}
file.append(buf, 0, stream.gcount());
return file;
}
#endif // HELPER_HPP_

View File

@ -1,226 +0,0 @@
#include <filesystem>
#include <fstream>
#include <iostream>
#include <iterator>
#include <unordered_map>
#include <fmt/core.h>
#include <gtkmm.h>
#include <json.hpp>
namespace fs = std::filesystem;
Glib::RefPtr<Gtk::Application> g_app;
Glib::RefPtr<Gtk::Builder> g_refGlade;
Gtk::Window* g_refWindow;
nlohmann::json preferences;
std::pair<std::string, std::string> tokenize(std::string str, std::string delim) {
int start = 0;
int end = str.find(delim);
std::string key{};
while (end != -1) {
key = str.substr(start, end - start);
start = end + delim.size();
end = str.find(delim, start);
}
return {key, str.substr(start, end - start)};
}
std::size_t replace_all(std::string& inout, std::string_view what, std::string_view with) {
std::size_t count{};
for (std::string::size_type pos{};
inout.npos != (pos = inout.find(what.data(), pos, what.length()));
pos += with.length(), ++count) {
inout.replace(pos, what.length(), with.data(), with.length());
}
return count;
}
std::size_t remove_all(std::string& inout, std::string_view what) {
return replace_all(inout, what, "");
}
// Read informations from the lsb-release file.
//
// @Returns args from lsb-release file
std::array<std::string, 2> get_lsb_infos() {
std::unordered_map<std::string, std::string> lsb{};
try {
std::ifstream lsb_release("/etc/lsb-release");
std::string line;
while (std::getline(lsb_release, line)) {
if (line.find("=") != std::string::npos) {
auto var = tokenize(line, "=");
remove_all(var.first, "DISTRIB_");
remove_all(var.second, "\"");
lsb[var.first] = var.second;
}
}
} catch (const std::exception& e) {
std::cerr << e.what() << '\n';
return {"not CachyOS", "0.0"};
}
return {lsb["ID"], lsb["RELEASE"]};
}
void on_action_clicked(GtkWidget* widget) {
const auto& name = gtk_widget_get_name(widget);
if (strncmp(name, "install", 7) == 0) {
fmt::print("install\n");
return;
} else if (strncmp(name, "autostart", 9) == 0) {
//const auto& action = GTK_ACTION(widget);
//set_autostart(action->get_active());
fmt::print("autostart\n");
return;
}
Gtk::AboutDialog* dialog;
g_refGlade->get_widget<Gtk::AboutDialog>("aboutdialog", std::ref(dialog));
dialog->set_decorated(false);
dialog->run();
dialog->hide();
}
void on_btn_clicked(GtkWidget* widget) {
const auto& name = gtk_widget_get_name(widget);
Gtk::Stack* stack;
g_refGlade->get_widget<Gtk::Stack>("stack", std::ref(stack));
stack->set_visible_child(fmt::format("{}page", name).c_str());
}
void on_link_clicked(GtkWidget* widget) {
const auto& name = gtk_widget_get_name(widget);
const std::string uri = preferences["urls"][name];
gtk_show_uri_on_window(nullptr, uri.c_str(), GDK_CURRENT_TIME, nullptr);
}
void on_delete_window(GtkWidget* widget) {
g_app->quit();
}
nlohmann::json read_json(const std::string& path) {
// read a JSON file
std::ifstream i(path);
nlohmann::json j;
i >> j;
return j;
}
int main(int argc, char** argv) {
g_app = Gtk::Application::create();
auto screen = Gdk::Screen::get_default();
bool is_dev = false;
if (argc > 1 && (strncmp(argv[1], "--dev", 5) == 0)) {
is_dev = true;
}
// Load preferences
if (is_dev) {
preferences = read_json("data/preferences.json");
preferences["data_path"] = "data/";
preferences["desktop_path"] = (fs::current_path() / "cachyos-hello.desktop").string();
preferences["locale_path"] = "locale/";
preferences["ui_path"] = "ui/cachyos-hello.glade";
preferences["style_path"] = "ui/style.css";
} else {
preferences = read_json("/usr/share/cachyos-hello/data/preferences.json");
}
// Get saved infos
//const auto& save = read_json(preferences["save_path"]);
// Import Css
auto provider = Gtk::CssProvider::create();
provider->load_from_path(preferences["style_path"]);
Gtk::StyleContext::add_provider_for_screen(screen, provider, GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
// Init window
g_refGlade = Gtk::Builder::create_from_file(preferences["ui_path"]);
gtk_builder_add_callback_symbol(g_refGlade->gobj(), "on_action_clicked", G_CALLBACK(on_action_clicked));
gtk_builder_add_callback_symbol(g_refGlade->gobj(), "on_btn_clicked", G_CALLBACK(on_btn_clicked));
gtk_builder_add_callback_symbol(g_refGlade->gobj(), "on_link_clicked", G_CALLBACK(on_link_clicked));
gtk_builder_add_callback_symbol(g_refGlade->gobj(), "on_delete_window", G_CALLBACK(on_delete_window));
gtk_builder_connect_signals(g_refGlade->gobj(), nullptr);
g_refGlade->get_widget<Gtk::Window>("window", std::ref(g_refWindow));
// Subtitle of headerbar
Gtk::HeaderBar* header;
g_refGlade->get_widget<Gtk::HeaderBar>("headerbar", std::ref(header));
const auto& lsb_info = get_lsb_infos();
header->set_subtitle(lsb_info[0] + " " + lsb_info[1]);
// Load images
if (fs::is_regular_file(preferences["logo_path"])) {
const auto& logo = Gdk::Pixbuf::create_from_file(preferences["logo_path"]);
g_refWindow->set_icon(logo);
Gtk::Image* image;
g_refGlade->get_widget<Gtk::Image>("distriblogo", std::ref(image));
image->set(logo);
Gtk::AboutDialog* dialog;
g_refGlade->get_widget<Gtk::AboutDialog>("aboutdialog", std::ref(dialog));
dialog->set_logo(logo);
}
Gtk::Box* social_box;
g_refGlade->get_widget<Gtk::Box>("social", std::ref(social_box));
for (const auto& btn : social_box->get_children()) {
const auto& name = btn->get_name();
const auto& icon_path = fmt::format("{}img/{}.png", preferences["data_path"], name.c_str());
Gtk::Image* image;
g_refGlade->get_widget<Gtk::Image>(name, image);
image->set(icon_path);
}
Gtk::Grid* homepage_grid;
g_refGlade->get_widget<Gtk::Grid>("homepage", std::ref(homepage_grid));
for (const auto& widget : homepage_grid->get_children()) {
if (!G_TYPE_CHECK_INSTANCE_TYPE(widget->gobj(), GTK_TYPE_BUTTON)) {
continue;
}
const auto& casted_widget = GTK_BUTTON(widget->gobj());
if (gtk_button_get_image_position(casted_widget) != GtkPositionType::GTK_POS_RIGHT) {
continue;
}
Gtk::Image image(fmt::format("{}/img/external-link.png", preferences["data_path"]));
image.set_margin_start(2);
gtk_button_set_image(casted_widget, (GtkWidget*)image.gobj());
}
// Create pages
const auto& pages = fmt::format("{}/pages/{}", preferences["data_path"], preferences["default_locale"]);
for (const auto& page : fs::directory_iterator(pages)) {
Gtk::ScrolledWindow scrolled_window;
Gtk::Viewport viewport(Gtk::Adjustment::create(1, 1, 1), Gtk::Adjustment::create(1, 1, 1));
Gtk::Label label;
label.set_line_wrap(true);
Gtk::Image image(Gtk::Stock::GO_BACK, Gtk::ICON_SIZE_BUTTON);
Gtk::Button backBtn;
backBtn.set_image(image);
backBtn.set_name("home");
backBtn.signal_clicked().connect(sigc::bind(sigc::ptr_fun(on_btn_clicked), (GtkWidget*)&backBtn));
Gtk::Grid grid;
grid.attach(backBtn, 0, 1, 1, 1);
grid.attach(label, 1, 2, 1, 1);
viewport.add(grid);
scrolled_window.add(viewport);
scrolled_window.show_all();
Gtk::Stack* stack;
g_refGlade->get_widget<Gtk::Stack>("stack", std::ref(stack));
stack->add(scrolled_window, page.path().stem().string() + "page");
}
// Shows the window and returns when it is closed.
return g_app->run(*g_refWindow);
}

10
src/main.cpp Normal file
View File

@ -0,0 +1,10 @@
#include "hello.hpp"
int main(int argc, char** argv) {
auto app = Gtk::Application::create();
Hello hello(argc, argv);
// Shows the window and returns when it is closed.
return app->run(hello);
}