diff --git a/README.md b/README.md index 2ded254..8ae3a16 100644 --- a/README.md +++ b/README.md @@ -1,59 +1,40 @@ # Chaotic Installer - -This a port Anarchy Linux to C++ using Qt with QML. -It's recommended to use with EGLFS as platform abstraction. - -## What it shares with Anarchy Linux? - - * This text is valid for both projects: - -Anarchy Linux is an Arch Linux installer providing a hassle-free pure installation and polished user experience. -Every aspect of the install is taken into account from partitioning and general system configuration, -to installing your favorite DE/WM and additional software from the official Arch Linux repos. - -The Anarchy installer is intended to provide both novice and experienced Linux users a simple and pain free way to install Arch Linux. -Install when you want it, where you want it, and how you want it. -That is the Anarchy philosophy. - -Anarchy aims to provide a polished and pure Arch install while leaving open every possible configuration avenue for the user to choose from. - -# What is different from Anarchy Linux? - - * Chaotic Installer has a Graphical Interface (still light and fast). - * Chaotic Installer is written in C++, has qt as deps, needs compilation, doesn't give you a terminal, and because of that it's hard to say it still follows KISS philosophy. - * It doesn't includes `yay`. - -# Is it related to Chaotic-AUR repository? - -Nope. And if it worries you, this installer doesn't include Chaotic-AUR or it's pacakges. -But, you'll find a ready to burn ISO with Chaotic Installer available in chaotic-aur (soon), and both projects are mantained by the same Pedro. - -# Why EGLFS? - -So you won't need Wayland, neither X nor a WM. - -# Dependencies +A setup wizard for ArchLinux using Qt and compatible with EGLFS. + +# Features + * Compatible with EGLFS: + * Does not require X11 or Wayland to run. + * Lazy scriptable setup: + * At the end of the script you'll be presented to an editable bash script containing all the instructions that will be used during setup. + * The same happens for networks connections and partitioning. + * No package will be installed without your consent. + +# Building +## Dependencies ``` yay -S qt5-base qt5-quickcontrols2 libinput libxkbcommon ``` -# How to compile? +## How to build? ``` yay -S qt5-tools cd chaotic-installer-qt qmake -project chaotic-installer.pro make +lrelease chaotic-installer.pro ``` -# How to run without X/Wayland? +## How to run without X/Wayland? Open a TTY without an open graphical session (`Ctrl+Alt+F3` should do it): ``` +export QT_QPA_PLATFORM='eglfs' ./bin/chaotic-installer ``` -There is also a `launcher/startchaotic` script that will be used in generated ISO. - # What it looks like? - ![Language Picking Screen](screenshot.png) + +# Is it related to Chaotic-AUR repository? +Both projects are mantained by the same Pedro, and you'll find a ready to burn ISO with Chaotic Installer available in Chaotic-AUR (soon). +But, this installer doesn't include Chaotic-AUR, any of it's packages, and won't offer to add it. \ No newline at end of file diff --git a/chaotic-installer.pro b/chaotic-installer.pro index 0199c5a..76bc2af 100755 --- a/chaotic-installer.pro +++ b/chaotic-installer.pro @@ -9,11 +9,13 @@ VPATH *= $${BASEDIR}/src TRANSLATIONS = langs/en_US.ts langs/pt_BR.ts HEADERS = chaotic-installer.hpp \ - lib/language.hpp lib/locale.hpp \ - lib/network.hpp lib/mirrors.hpp + lib/translations.hpp lib/keymap.hpp \ + lib/network.hpp lib/mirrors.hpp \ + lib/locales.hpp SOURCES = main.cpp \ - lib/language.cpp lib/locale.cpp \ - lib/network.cpp lib/mirrors.cpp + lib/translations.cpp lib/keymap.cpp \ + lib/network.cpp lib/mirrors.cpp \ + lib/locales.cpp TARGET = bin/chaotic-installer lupdate_only { diff --git a/langs/en_US.ts b/langs/en_US.ts index 1455ea8..f3546b3 100644 --- a/langs/en_US.ts +++ b/langs/en_US.ts @@ -64,6 +64,25 @@ If unsure leave default + + LocalesSelect + + Select enabled locales: + + + + Select system language: + + + + Cancel + + + + Next + + + MirrorsSort diff --git a/langs/pt_BR.ts b/langs/pt_BR.ts index 86694e4..20c93cd 100644 --- a/langs/pt_BR.ts +++ b/langs/pt_BR.ts @@ -5,7 +5,7 @@ EthernetMenu Select adapter: - Selecione a interface: + Selecione a interface: Generate profile @@ -138,6 +138,25 @@ Caso não tenha certeza, deixe o padrão Próximo + + LocalesSelect + + Select enabled locales: + Selecione as internacionalizações: + + + Select system language: + Selecione o idioma do sistema: + + + Cancel + Cancelar + + + Next + Próximo + + MirrorsSort @@ -198,11 +217,11 @@ um script para o netctl, esse script poderá ser editado antes de conectar.WifiMenu Select adapter: - Selecione uma interface: + Selecione uma interface: Select SSID: - Selecione um SSID: + Selecione um SSID: Refresh @@ -210,7 +229,7 @@ um script para o netctl, esse script poderá ser editado antes de conectar. Password: - Senha: + Senha: WPA2 personal password only diff --git a/qml/KeyboardPicker.qml b/qml/KeyboardPicker.qml index 9f2f526..9ce4594 100644 --- a/qml/KeyboardPicker.qml +++ b/qml/KeyboardPicker.qml @@ -52,9 +52,9 @@ Component { id: pickKeyboardList Repeater { - model: allKeyboards + model: keymap.allKeyboards - RadioButton {text: modelData; onClicked: setupLocale.selectedKeyboard = modelData } + RadioButton {text: modelData; onClicked: keymap.selectedKeyboard = modelData } } } } diff --git a/qml/LanguagePicker.qml b/qml/LanguagePicker.qml index 6a498ee..cb74df5 100644 --- a/qml/LanguagePicker.qml +++ b/qml/LanguagePicker.qml @@ -29,7 +29,6 @@ Component { anchors.bottomMargin: 20 width: parent.width - //Flickable { ScrollView { anchors.top: parent.top anchors.bottom: parent.bottom @@ -51,7 +50,7 @@ Component { Column { id: pickLangList - RadioButton {text: qsTr('English'); checked: true; onClicked: lang.setLang("en_US") } + RadioButton {text: qsTr('English'); checked: true; onClicked: translations.setLang("en_US") } /*RadioButton {text: qsTr('Bulgarian')} RadioButton {text: qsTr('Dutch')} RadioButton {text: qsTr('French')} @@ -64,7 +63,7 @@ Component { RadioButton {text: qsTr('Lithuanian')} RadioButton {text: qsTr('Polish')} RadioButton {text: qsTr('Portuguese')}*/ - RadioButton {text: qsTr('Portuguese (Brazilian)'); onClicked: lang.setLang("pt_BR") } + RadioButton {text: qsTr('Portuguese (Brazilian)'); onClicked: translations.setLang("pt_BR") } /*RadioButton {text: qsTr('Romanian')} RadioButton {text: qsTr('Russian')} RadioButton {text: qsTr('Spanish')} diff --git a/qml/LocalesSelect.qml b/qml/LocalesSelect.qml new file mode 100644 index 0000000..bd8e086 --- /dev/null +++ b/qml/LocalesSelect.qml @@ -0,0 +1,111 @@ +/* Chaotic Installer Locales Selecting Screen + + I like to write this by hand, do whatever you want, but keep diff clean to read, so I + can keep writing it manually. + + Also, consider this file copyright-free. +*/ + +import QtQuick 2.9 +import QtQml.Models 2.2 +import QtQuick.Controls 2.12 +import QtQuick.Controls.Material 2.12 + +Component { + Column { + anchors.fill: parent + anchors.leftMargin: 20 + anchors.rightMargin: 20 + + Row { + id: localesHeader + anchors.top: parent.top + anchors.topMargin: 20 + anchors.horizontalCenter: parent.horizontalCenter + + Column { + spacing: 20 + + Row { + anchors.horizontalCenter: parent.horizontalCenter + + Text { + text: qsTr('Select enabled locales:') + } + } + } + } + + Row { + id: localeListView + anchors.top: localesHeader.bottom + anchors.bottom: langPicker.top + anchors.left: parent.left + anchors.right: parent.right + anchors.bottomMargin: 20 + ScrollView { + anchors.fill:parent + clip: true + + contentWidth: localeList.width + contentHeight: localeList.height + + ScrollBar.vertical: ScrollBar { + policy: ScrollBar.AlwaysOn; + parent: localeListView + anchors.top: parent.top + anchors.right: parent.right + anchors.bottom: parent.bottom + } + + ListView { + id: localeList + width: parent.width + model: locales.codes + delegate: CheckBox { + text: modelData + checked: false + onClicked: locales.setEnabled(index, checked) + } + } + } + } + + Row { + id: langPicker + anchors.bottom: localesFooter.top + anchors.bottomMargin: 20 + + Column { + anchors.verticalCenter: parent.verticalCenter + Text { text: qsTr('Select system language: ') } + } + Column { + ComboBox { + model: locales.langs + onActivated: locales.setLANG(currentIndex) + } + } + } + + Row { + id: localesFooter + anchors.bottom: parent.bottom + anchors.bottomMargin: 20 + anchors.horizontalCenter: parent.horizontalCenter + spacing: 80 + + + Button { + text: qsTr('Cancel') + onClicked: contentStack.pop() + } + + Button { + text: qsTr('Next') + highlighted: true + onClicked: contentStack.push(nextMenu) + } + } + } +} \ No newline at end of file diff --git a/qml/MainContainer.qml b/qml/MainContainer.qml index 324f1c3..30c03e7 100644 --- a/qml/MainContainer.qml +++ b/qml/MainContainer.qml @@ -81,4 +81,5 @@ Rectangle { WifiMenu { id: wifiMenu } EthernetMenu { id: ethMenu } MirrorsSort { id: mirrorsSort } + LocalesSelect { id: localesSelect } } \ No newline at end of file diff --git a/qml/MirrorsSort.qml b/qml/MirrorsSort.qml index 3ed9384..f1e796b 100644 --- a/qml/MirrorsSort.qml +++ b/qml/MirrorsSort.qml @@ -155,6 +155,7 @@ Component { id: mirrorsFooter anchors.bottom: parent.bottom anchors.bottomMargin: 20 + anchors.topMargin: 20 anchors.horizontalCenter: parent.horizontalCenter spacing: 80 @@ -166,9 +167,11 @@ Component { Button { text: qsTr('Apply && Next') + highlighted: true + //[TODO] Require at least one active mirror! onClicked: { mirrors.apply() - contentStack.push(nextMenu) + contentStack.push(localesSelect) } } } diff --git a/src/chaotic-installer.hpp b/src/chaotic-installer.hpp index b7cec1f..a6138ea 100644 --- a/src/chaotic-installer.hpp +++ b/src/chaotic-installer.hpp @@ -4,19 +4,4 @@ #define QML_PATH "qml/" #define ASSETS_PATH "assets/" -void set_keys(); -void update_mirrors(); -void check_connection(); -void set_locale(); -void set_zone(); -void prepare_drives(); -void install_options(); -void set_hostname(); -void set_user(); -void add_software(); -void install_base(); -void configure_system(); -void add_user(); -void reboot_system(); - #endif // CHAOTIC_INSTALLER_H \ No newline at end of file diff --git a/src/lib/locale.cpp b/src/lib/keymap.cpp similarity index 54% rename from src/lib/locale.cpp rename to src/lib/keymap.cpp index 2e809a1..df26c17 100644 --- a/src/lib/locale.cpp +++ b/src/lib/keymap.cpp @@ -2,28 +2,18 @@ #include #include -#include "locale.hpp" +#include "keymap.hpp" -Locale::Locale(QObject *parent) : - QObject (parent) {} +QDir Keymap::keymapDir = QDir(QStringLiteral("/usr/share/kbd/keymaps/i386/qwerty")); -void Locale::toggleLocale(QString locale) { - //TODO -} +Keymap::Keymap(QObject *parent) : + QObject (parent) {} -QString Locale::getSelectedKeyboard() { +QString Keymap::getSelectedKeyboard() { return selectedKeyboard; } -QStringList Locale::getSelectedLocales() { - return selectedLocales; -} - -QString Locale::getSelectedZone() { - return selectedZone; -} - -void Locale::setKeyboard(const QString &keymap) { +void Keymap::setKeyboard(const QString &keymap) { qDebug() << "Setting keymap " << keymap; selectedKeyboard = keymap; @@ -36,18 +26,13 @@ void Locale::setKeyboard(const QString &keymap) { emit keyboardChanged(); } -void Locale::setZone(const QString &zone) { - //TODO -} - -QStringList Locale::keyboardsCache = QStringList(); -QDir Locale::keymapDir = QDir(QStringLiteral("/usr/share/kbd/keymaps/i386/qwerty")); - -QStringList Locale::allKeyboards() const { +QStringList Keymap::allKeyboards() { if(keyboardsCache.size() < 1) { keyboardsCache = keymapDir .entryList(QStringList() << "*.map.gz") .replaceInStrings(".map.gz", ""); + emit allKeyboardsChanged(); } + return keyboardsCache; } \ No newline at end of file diff --git a/src/lib/keymap.hpp b/src/lib/keymap.hpp new file mode 100644 index 0000000..ef86ef6 --- /dev/null +++ b/src/lib/keymap.hpp @@ -0,0 +1,31 @@ +#ifndef KEYMAP_H +#define KEYMAP_H + +#include +#include + +class Keymap : public QObject { + Q_OBJECT + Q_PROPERTY(QString selectedKeyboard READ getSelectedKeyboard WRITE setKeyboard NOTIFY keyboardChanged) + Q_PROPERTY(QStringList allKeyboards READ allKeyboards NOTIFY allKeyboardsChanged) + +private: + QString selectedKeyboard; + QStringList keyboardsCache; + static QDir keymapDir; + +public: + explicit Keymap(QObject *parent = nullptr); + + void setKeyboard(const QString &keymap); + void setZone(const QString &locale); + + QStringList allKeyboards(); + QString getSelectedKeyboard(); + +signals: + void keyboardChanged(); + void allKeyboardsChanged(); +}; + +#endif // KEYMAP_H \ No newline at end of file diff --git a/src/lib/locale.hpp b/src/lib/locale.hpp deleted file mode 100644 index 803e1d9..0000000 --- a/src/lib/locale.hpp +++ /dev/null @@ -1,37 +0,0 @@ -#ifndef LOCALE_H -#define LOCALE_H - -#include -#include - -class Locale : public QObject { - Q_OBJECT - Q_PROPERTY(QString selectedKeyboard READ getSelectedKeyboard WRITE setKeyboard NOTIFY keyboardChanged) - Q_PROPERTY(QStringList selectedLocales READ getSelectedLocales NOTIFY localesChanged) - Q_PROPERTY(QString selectedZone READ getSelectedZone WRITE setZone NOTIFY zoneChanged) - -private: - QString selectedKeyboard; - QStringList selectedLocales; - QString selectedZone; - static QStringList keyboardsCache; - static QDir keymapDir; - -public: - explicit Locale(QObject *parent = nullptr); - - void setKeyboard(const QString &keymap); - void setZone(const QString &locale); - Q_INVOKABLE void toggleLocale(const QString locale); - - QStringList allKeyboards() const; - QString getSelectedKeyboard(); - QStringList getSelectedLocales(); - QString getSelectedZone(); -signals: - void keyboardChanged(); - void localesChanged(); - void zoneChanged(); -}; - -#endif // LOCALE_H \ No newline at end of file diff --git a/src/lib/locales.cpp b/src/lib/locales.cpp new file mode 100644 index 0000000..d61af84 --- /dev/null +++ b/src/lib/locales.cpp @@ -0,0 +1,100 @@ +#include +#include +#include +#include + +#include "locales.hpp" + +Locales::Locales(QObject *parent) : QObject (parent), selectedLANG("C") {} + +QList Locales::getLocales() { + if(localesList.size() < 1) { + QProcess process = QProcess(); + process.setReadChannel(QProcess::StandardOutput); + process.start(QStringLiteral("grep -Po \"(?<=^#|^)[a-z]+(?:_[A-Z]+)?(?:@[a-z]+|.[A-Z0-9-]+)? [A-Z0-9-]+\" /etc/locale.gen")); + + QByteArray data; + while(process.waitForFinished()) + data.append(process.readAll()); + + QStringList txtList = QTextCodec::codecForMib(106)->toUnicode(data.data()).split("\n"); + txtList.removeAll(QString("")); + + for (const auto& code : txtList) + localesList << new LocaleEntry(code); + + emit langsChanged(); + } + return localesList; +} + +LocaleEntry::LocaleEntry(QString ncode, QObject *parent) : QObject (parent), code(ncode), enabled(false) {} + +QString LocaleEntry::getCode() { + return code; +} + +QString LocaleEntry::getLANG() { + if(langCode.size() < 1) { + langCode = QString(code); + langCode.remove(QRegExp(" [A-Z0-9-]+")); + } + return langCode; +} + +bool LocaleEntry::isEnabled() { + return enabled; +} +void LocaleEntry::setEnabled(bool state) { + enabled = state; + + emit toggled(); +} + +// that's not how I wanted to do this, but I don't want to do a QAbstractItemModel +// I'll be happy if you do it for me! +QStringList Locales::getLocalesCodes() { + QStringList _; + for (const auto item : getLocales()) + _ << item->getCode(); + return _; +} + +void Locales::setEnabled(int i, bool en) { + localesList.at(i)->setEnabled(en); + selectedLANG = "C"; + emit langsChanged(); +} + +QString Locales::genLocaleGenFile() { + QString locale_file; + QTextStream locale_out(&locale_file, QIODevice::WriteOnly | QIODevice::Text); + for (const auto item : getLocales()) + locale_out << (item->isEnabled() ? "" : "#") << item->getCode() << "\n"; + return locale_file; +} + +QString Locales::genLocaleConfFile() { + QString conf_file; + QTextStream conf(&conf_file, QIODevice::WriteOnly | QIODevice::Text); + conf << "LANG=" << getLANG() << "\n"; + return conf_file; + +} + +QStringList Locales::getAvailableLANGs() { + QStringList _; + _ << "C"; + for (const auto item : getLocales()) + if(item->isEnabled()) + _ << item->getLANG(); + return _; +} + +QString Locales::getLANG() { + return selectedLANG; +} + +void Locales::setLANG(int lang_i) { + selectedLANG = getAvailableLANGs().at(lang_i); +} \ No newline at end of file diff --git a/src/lib/locales.hpp b/src/lib/locales.hpp new file mode 100644 index 0000000..7cdd742 --- /dev/null +++ b/src/lib/locales.hpp @@ -0,0 +1,49 @@ +#ifndef LOCALES_H +#define LOCALES_H + +#include +#include + +class LocaleEntry : public QObject { + Q_OBJECT + Q_PROPERTY(QString code READ getCode) + Q_PROPERTY(QString lang READ getLANG) + Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled NOTIFY toggled) +private: + QString code; + QString langCode; + bool enabled; +public: + explicit LocaleEntry(QString code, QObject *parent = nullptr); + Q_INVOKABLE QString getCode(); + Q_INVOKABLE QString getLANG(); + Q_INVOKABLE bool isEnabled(); + Q_INVOKABLE void setEnabled(bool); +signals: + void toggled(); +}; + +class Locales : public QObject { + Q_OBJECT + Q_PROPERTY(QList list READ getLocales) + Q_PROPERTY(QStringList codes READ getLocalesCodes) + Q_PROPERTY(QStringList langs READ getAvailableLANGs NOTIFY langsChanged) + Q_PROPERTY(QString selectedLANG READ getLANG) +private: + QList localesList; + QString selectedLANG; +public: + explicit Locales(QObject *parent = nullptr); + QList getLocales(); + QStringList getLocalesCodes(); + QStringList getAvailableLANGs(); + QString getLANG(); + Q_INVOKABLE void setEnabled(int, bool); + Q_INVOKABLE void setLANG(int); + Q_INVOKABLE QString genLocaleConfFile(); + Q_INVOKABLE QString genLocaleGenFile(); +signals: + void langsChanged(); +}; + +#endif // LOCALES_H \ No newline at end of file diff --git a/src/lib/language.cpp b/src/lib/translations.cpp similarity index 71% rename from src/lib/language.cpp rename to src/lib/translations.cpp index 05ec19a..6241329 100644 --- a/src/lib/language.cpp +++ b/src/lib/translations.cpp @@ -1,14 +1,14 @@ -#include "language.hpp" #include #include -Lang::Lang(QGuiApplication* app, QQmlEngine * engine, QObject *parent) : +#include "translations.hpp" + +Translations::Translations(QGuiApplication* app, QQmlEngine * engine, QObject *parent) : QObject (parent), app(app), engine(engine) { translator = new QTranslator(this); - //setLang(LOCALE_DEFAULT); } -void Lang::setLang(QString locale) { +void Translations::setLang(QString locale) { QString path = QStringLiteral("langs/%1.qm") .arg(locale); @@ -27,7 +27,7 @@ void Lang::setLang(QString locale) { emit languageChanged(); } -QString Lang::getSelectedLang() +QString Translations::getLang() { return selectedLang; } diff --git a/src/lib/language.hpp b/src/lib/translations.hpp similarity index 55% rename from src/lib/language.hpp rename to src/lib/translations.hpp index 791d02b..dde98a5 100644 --- a/src/lib/language.hpp +++ b/src/lib/translations.hpp @@ -1,5 +1,5 @@ -#ifndef LANGUAGE_H -#define LANGUAGE_H +#ifndef TRANSLATIONS_H +#define TRANSLATIONS_H #include #include @@ -7,11 +7,9 @@ #include #include -#define LOCALE_DEFAULT "en_US" - -class Lang : public QObject { +class Translations : public QObject { Q_OBJECT - Q_PROPERTY(QString selectedLang READ getSelectedLang NOTIFY languageChanged) + Q_PROPERTY(QString selectedLang READ getLang WRITE setLang NOTIFY languageChanged) private: QGuiApplication *app; @@ -20,14 +18,14 @@ class Lang : public QObject { QString selectedLang; public: - explicit Lang(QGuiApplication *app, QQmlEngine *engine, + explicit Translations(QGuiApplication *app, QQmlEngine *engine, QObject *parent = nullptr); - QString getSelectedLang(); + QString getLang(); Q_INVOKABLE void setLang(const QString locale); signals: void languageChanged(); }; -#endif // LANGUAGE_H \ No newline at end of file +#endif // TRANSLATIONS_H \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 15b45c0..5e4ed48 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -30,10 +30,11 @@ #include #include "chaotic-installer.hpp" -#include "lib/language.hpp" -#include "lib/locale.hpp" +#include "lib/translations.hpp" +#include "lib/keymap.hpp" #include "lib/network.hpp" #include "lib/mirrors.hpp" +#include "lib/locales.hpp" int main(int argc, char *argv[]) { @@ -53,20 +54,19 @@ int main(int argc, char *argv[]) app.installTranslator(translator); // Components - //Component * m_compo = new Component(&view); - Lang * m_lang = new Lang(&app, view.engine(), &view); - Locale * m_locale = new Locale(&view); + Translations * m_translations = new Translations(&app, view.engine(), &view); + Keymap * m_keymap = new Keymap(&view); Network * m_net = new Network(&view); Mirrors * m_mirrors = new Mirrors(&view); + Locales * m_locales = new Locales(&view); // Context props - //context->setContextProperty(QStringLiteral("compo"), m_compo); - context->setContextProperty(QStringLiteral("lang"), m_lang); - context->setContextProperty(QStringLiteral("setupLocale"), m_locale); + context->setContextProperty(QStringLiteral("translations"), m_translations); + context->setContextProperty(QStringLiteral("keymap"), m_keymap); context->setContextProperty(QStringLiteral("net"), m_net); context->setContextProperty(QStringLiteral("mirrors"), m_mirrors); + context->setContextProperty(QStringLiteral("locales"), m_locales); context->setContextProperty(QStringLiteral("assetsPath"), appPath.resolved(QStringLiteral(ASSETS_PATH))); - context->setContextProperty(QStringLiteral("allKeyboards"), QVariant::fromValue(m_locale->allKeyboards())); // Some connections QObject::connect(view.engine(), &QQmlApplicationEngine::quit, &QGuiApplication::quit);