diff --git a/selfdrive/ui/SConscript b/selfdrive/ui/SConscript index 4c2a36db87..414f862823 100644 --- a/selfdrive/ui/SConscript +++ b/selfdrive/ui/SConscript @@ -71,7 +71,7 @@ else: else: qt_libs += [f"Qt5{m}" for m in qt_modules] - qt_src = ["qt/ui.cc", "qt/window.cc", "qt/qt_sound.cc", "qt/offroad/settings.cc", "qt/offroad/onboarding.cc", "qt/offroad/wifi.cc", "qt/offroad/wifiManager.cc"] + src + qt_src = ["qt/ui.cc", "qt/window.cc", "qt/qt_sound.cc", "qt//offroad/keyboard.cc", "qt/offroad/input_field.cc", "qt/offroad/settings.cc", "qt/offroad/onboarding.cc", "qt/offroad/wifi.cc", "qt/offroad/wifiManager.cc"] + src qt_env.Program("_ui", qt_src, LIBS=qt_libs + libs) # spinner and text window diff --git a/selfdrive/ui/qt/offroad/input_field.cc b/selfdrive/ui/qt/offroad/input_field.cc new file mode 100644 index 0000000000..4696416eaf --- /dev/null +++ b/selfdrive/ui/qt/offroad/input_field.cc @@ -0,0 +1,56 @@ +#include +#include +#include +#include +#include + +#include "input_field.hpp" +#include "keyboard.hpp" + +InputField::InputField(QWidget *parent): QWidget(parent) { + l = new QVBoxLayout(); + QHBoxLayout *r = new QHBoxLayout(); + label = new QLabel(this); + label->setText("password"); + r->addWidget(label); + QPushButton* cancel = new QPushButton("cancel"); + QObject::connect(cancel, SIGNAL(released()), this, SLOT(emitEmpty())); + cancel->setFixedHeight(150); + cancel->setFixedWidth(300); + r->addWidget(cancel); + l->addLayout(r); + l->addSpacing(80); + + line = new QLineEdit(""); + l->addWidget(line); + l->addSpacing(200); + + k = new Keyboard(this); + QObject::connect(k, SIGNAL(emitButton(QString)), this, SLOT(getText(QString))); + l->addWidget(k); + setLayout(l); +} + +void InputField::emitEmpty(){ + emitText(""); + line->setText(""); +} +void InputField::getText(QString s){ + if(!QString::compare(s,"⌫")){ + line->backspace(); + } + + if(!QString::compare(s,"⏎")){ + emitText(line->text()); + line->setText(""); + } + + QVector control_buttons {"⇧", "↑", "ABC", "⏎", "#+=", "⌫", "123"}; + for(QString c :control_buttons){ + if(!QString::compare(s, c)){ + return; + } + } + line->insert(s.left(1)); +} + diff --git a/selfdrive/ui/qt/offroad/input_field.hpp b/selfdrive/ui/qt/offroad/input_field.hpp new file mode 100644 index 0000000000..a56bac2619 --- /dev/null +++ b/selfdrive/ui/qt/offroad/input_field.hpp @@ -0,0 +1,29 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include "keyboard.hpp" + +class InputField : public QWidget { + Q_OBJECT + +public: + explicit InputField(QWidget* parent = 0); + QLabel *label; + +private: + QLineEdit *line; + Keyboard *k; + QVBoxLayout *l; + +public slots: + void emitEmpty(); + void getText(QString s); + +signals: + void emitText(QString s); +}; diff --git a/selfdrive/ui/qt/offroad/keyboard.cc b/selfdrive/ui/qt/offroad/keyboard.cc new file mode 100644 index 0000000000..e74c8102c7 --- /dev/null +++ b/selfdrive/ui/qt/offroad/keyboard.cc @@ -0,0 +1,117 @@ +#include +#include +#include +#include +#include +#include + +#include "keyboard.hpp" + +KeyboardLayout::KeyboardLayout(QWidget* parent, std::vector> layout) : QWidget(parent) { + QVBoxLayout* vlayout = new QVBoxLayout; + QButtonGroup* btn_group = new QButtonGroup(this); + + QObject::connect(btn_group, SIGNAL(buttonClicked(QAbstractButton*)), parent, SLOT(handleButton(QAbstractButton*))); + + int i = 0; + for(auto s : layout){ + QHBoxLayout *hlayout = new QHBoxLayout; + + if (i == 1){ + hlayout->addSpacing(50); + } + + for(QString p : s){ + QPushButton* btn = new QPushButton(p); + btn->setFixedHeight(100); + + if (p == QString(" ")){ + btn->setFixedWidth(1024); + } + + btn_group->addButton(btn); + hlayout->addSpacing(5); + hlayout->addWidget(btn); + } + + if (i == 1){ + hlayout->addSpacing(50); + } + + vlayout->addLayout(hlayout); + i++; + } + + setLayout(vlayout); +} + +Keyboard::Keyboard(QWidget *parent) : QWidget(parent) { + main_layout = new QStackedLayout; + + // lowercase + std::vector> lowercase = { + {"q","w","e","r","t","y","u","i","o","p"}, + {"a","s","d","f","g","h","j","k","l"}, + {"⇧","z","x","c","v","b","n","m","⌫"}, + {"123"," ","⏎"}, + }; + main_layout->addWidget(new KeyboardLayout(this, lowercase)); + + // uppercase + std::vector> uppercase = { + {"Q","W","E","R","T","Y","U","I","O","P"}, + {"A","S","D","F","G","H","J","K","L"}, + {"↑","Z","X","C","V","B","N","M","⌫"}, + {"123"," ","⏎"}, + }; + main_layout->addWidget(new KeyboardLayout(this, uppercase)); + + // 1234567890 + std::vector> numbers = { + {"1","2","3","4","5","6","7","8","9","0"}, + {"-","/",":",";","(",")","$","&&","@","\""}, + {"#+=",".",",","?","!","`","⌫"}, + {"ABC"," ","⏎"}, + }; + main_layout->addWidget(new KeyboardLayout(this, numbers)); + + // Special characters + std::vector> specials = { + {"[","]","{","}","#","%","^","*","+","="}, + {"_","\\","|","~","<",">","€","£","¥"," "}, + {"123",".",",","?","!","`","⌫"}, + {"ABC"," ","⏎"}, + }; + main_layout->addWidget(new KeyboardLayout(this, specials)); + + setLayout(main_layout); + main_layout->setCurrentIndex(0); + + setStyleSheet(R"( + QPushButton { font-size: 40px } + * { + background-color: #99777777; + } + )"); +} + + +void Keyboard::handleButton(QAbstractButton* m_button){ + QString id = m_button->text(); + if(!QString::compare(m_button->text(),"↑")||!QString::compare(m_button->text(),"ABC")){ + main_layout->setCurrentIndex(0); + } + if(!QString::compare(m_button->text(),"⇧")){ + main_layout->setCurrentIndex(1); + } + if(!QString::compare(m_button->text(),"123")){ + main_layout->setCurrentIndex(2); + } + if(!QString::compare(m_button->text(),"#+=")){ + main_layout->setCurrentIndex(3); + } + if(!QString::compare(m_button->text(),"⏎")){ + main_layout->setCurrentIndex(0); + } + emit emitButton(m_button->text()); +} diff --git a/selfdrive/ui/qt/offroad/keyboard.hpp b/selfdrive/ui/qt/offroad/keyboard.hpp new file mode 100644 index 0000000000..f21d48700c --- /dev/null +++ b/selfdrive/ui/qt/offroad/keyboard.hpp @@ -0,0 +1,31 @@ +#pragma once + +#include + +#include +#include +#include +#include + +class KeyboardLayout : public QWidget { + Q_OBJECT + +public: + explicit KeyboardLayout(QWidget *parent, std::vector> layout); +}; + +class Keyboard : public QWidget { + Q_OBJECT + +public: + explicit Keyboard(QWidget *parent = 0); + +private: + QStackedLayout* main_layout; + +private slots: + void handleButton(QAbstractButton* m_button); + +signals: + void emitButton(QString s); +}; diff --git a/selfdrive/ui/qt/offroad/settings.cc b/selfdrive/ui/qt/offroad/settings.cc index 8711bf5c5e..a2ff596f4d 100644 --- a/selfdrive/ui/qt/offroad/settings.cc +++ b/selfdrive/ui/qt/offroad/settings.cc @@ -12,6 +12,7 @@ #include "wifi.hpp" #include "settings.hpp" +#include "input_field.hpp" #include "common/params.h" @@ -167,7 +168,7 @@ QWidget * device_panel() { } QWidget * developer_panel() { - QVBoxLayout *developer_layout = new QVBoxLayout; + QVBoxLayout *main_layout = new QVBoxLayout; // TODO: enable SSH toggle and github keys @@ -182,11 +183,11 @@ QWidget * developer_panel() { for (auto l : labels) { QString text = QString::fromStdString(l.first + ": " + l.second); - developer_layout->addWidget(new QLabel(text)); + main_layout->addWidget(new QLabel(text)); } QWidget *widget = new QWidget; - widget->setLayout(developer_layout); + widget->setLayout(main_layout); return widget; } diff --git a/selfdrive/ui/qt/offroad/wifi.cc b/selfdrive/ui/qt/offroad/wifi.cc index 610ec0805b..54b63a2d14 100644 --- a/selfdrive/ui/qt/offroad/wifi.cc +++ b/selfdrive/ui/qt/offroad/wifi.cc @@ -9,10 +9,12 @@ #include #include #include -#include +#include #include "wifi.hpp" #include "wifiManager.hpp" +#include "input_field.hpp" + CustomConnectButton::CustomConnectButton(QString text, int iid){ setText(text); id=iid; @@ -31,30 +33,37 @@ void clearLayout(QLayout* layout){ } WifiUI::WifiUI(QWidget *parent) : QWidget(parent) { - vlayout = new QVBoxLayout; wifi = new WifiManager; - refresh(); - setLayout(vlayout); - setStyleSheet(R"( - QLabel { font-size: 40px } - QPushButton:enabled { - background-color: #114265; - } - QPushButton:disabled { - background-color: #323C43; - } - * { - background-color: #114265; - } - )"); + QVBoxLayout * top_layout = new QVBoxLayout; + swidget = new QStackedWidget; + + // Networks page + wifi_widget = new QWidget; + vlayout = new QVBoxLayout; + wifi_widget->setLayout(vlayout); + swidget->addWidget(wifi_widget); + + // Keyboard page + a = new InputField(); + QObject::connect(a, SIGNAL(emitText(QString)), this, SLOT(receiveText(QString))); + swidget->addWidget(a); + swidget->setCurrentIndex(0); + + top_layout->addWidget(swidget); + setLayout(top_layout); + a->setStyleSheet(R"( + QLineEdit { + background-color: #114265; + } + )"); // TODO: implement (not) connecting with wrong password - // Update network list every second + // Update network list timer = new QTimer(this); QObject::connect(timer, SIGNAL(timeout()), this, SLOT(refresh())); - timer->start(1000); + timer->start(400); // Scan on startup wifi->request_scan(); @@ -91,7 +100,25 @@ void WifiUI::refresh(){ hlayout->addWidget(m_button); hlayout->addSpacing(20); - vlayout->addLayout(hlayout); + + QWidget * w = new QWidget; + w->setLayout(hlayout); + vlayout->addWidget(w); + + w->setStyleSheet(R"( + QLabel { + font-size: 40px + } + QPushButton:enabled { + background-color: #114265; + } + QPushButton:disabled { + background-color: #323C43; + } + * { + background-color: #114265; + } + )"); i+=1; } } @@ -99,26 +126,34 @@ void WifiUI::refresh(){ void WifiUI::handleButton(QAbstractButton* button){ CustomConnectButton* m_button = static_cast(button); int id = m_button->id; + qDebug()<seen_networks[id]; - // qDebug() << "Clicked a button:" << id; - // qDebug() << n.ssid; + a->label->setText("Password for "+n.ssid); if(n.security_type==SecurityType::OPEN){ wifi->connect(n); } else if (n.security_type==SecurityType::WPA){ - bool ok = false; - QString password; - -#ifdef QCOM2 - // TODO: implement touch keyboard -#else - password = QInputDialog::getText(this, "Password for "+n.ssid, "Password", QLineEdit::Normal, "", &ok); -#endif - if (ok){ + + QString password = getStringFromUser(); + if(password != ""){ wifi->connect(n, password); } - } else { qDebug() << "Cannot determine a network's security type"; } } + +QString WifiUI::getStringFromUser(){ + swidget->setCurrentIndex(1); + + loop.exec(); + + swidget->setCurrentIndex(0); + + return text; +} + +void WifiUI::receiveText(QString t){ + loop.quit(); + text = t; +} diff --git a/selfdrive/ui/qt/offroad/wifi.hpp b/selfdrive/ui/qt/offroad/wifi.hpp index 1caf2f4ee2..b88c9183e1 100644 --- a/selfdrive/ui/qt/offroad/wifi.hpp +++ b/selfdrive/ui/qt/offroad/wifi.hpp @@ -1,33 +1,46 @@ #pragma once #include "wifiManager.hpp" +#include "input_field.hpp" #include #include #include #include #include #include +#include #include class CustomConnectButton : public QPushButton{ public: - explicit CustomConnectButton(QString text, int iid); - int id; + explicit CustomConnectButton(QString text, int iid); + int id; }; class WifiUI : public QWidget { Q_OBJECT - private: - WifiManager* wifi; - QVBoxLayout* vlayout; - QTimer * timer; +private: + WifiManager* wifi; - public: - explicit WifiUI(QWidget *parent = 0); + QStackedWidget* swidget; + QVBoxLayout* vlayout; + QWidget * wifi_widget; - private slots: - void handleButton(QAbstractButton* m_button); - void refresh(); + InputField *a; + QEventLoop loop; + QTimer * timer; + QString text; + QString getStringFromUser(); + +public: + explicit WifiUI(QWidget *parent = 0); + +private slots: + void handleButton(QAbstractButton* m_button); + void refresh(); + void receiveText(QString text); +signals: + void gotText(); }; diff --git a/selfdrive/ui/qt/window.cc b/selfdrive/ui/qt/window.cc index 3aa0de55af..db20965a39 100644 --- a/selfdrive/ui/qt/window.cc +++ b/selfdrive/ui/qt/window.cc @@ -10,6 +10,9 @@ #include #include "window.hpp" +#include "offroad/input_field.hpp" +#include "offroad/settings.hpp" +#include "offroad/onboarding.hpp" #include "paint.hpp" #include "common/util.h" @@ -56,6 +59,9 @@ MainWindow::MainWindow(QWidget *parent) : QWidget(parent) { onboardingWindow = new OnboardingWindow(this); main_layout->addWidget(onboardingWindow); + InputField *inputField = new InputField(this); + main_layout->addWidget(inputField); + main_layout->setMargin(0); setLayout(main_layout); QObject::connect(glWindow, SIGNAL(openSettings()), this, SLOT(openSettings()));