Half-working setup

Huge thanks to @Szybet for writing at least 95% of the code in this commit
This commit is contained in:
Nicolas Mailloux 2022-06-17 23:59:21 -04:00
parent 9558de32e1
commit 763279f1f1
14 changed files with 1376 additions and 292 deletions

185
apps.cpp
View file

@ -1,8 +1,14 @@
#include "apps.h" #include "apps.h"
#include "ui_apps.h" #include "ui_apps.h"
#include "mainwindow.h" #include "mainwindow.h"
#include "userapps.h"
#include <QFile> #include <QFile>
#include <QProcess> #include <QProcess>
#include <QJsonParseError>
#include <QJsonObject>
#include <QJsonValue>
#include <QJsonArray>
apps::apps(QWidget *parent) : apps::apps(QWidget *parent) :
QWidget(parent), QWidget(parent),
@ -61,6 +67,14 @@ apps::apps(QWidget *parent) :
ui->lightmapsLaunchBtn->deleteLater(); ui->lightmapsLaunchBtn->deleteLater();
} }
ui->editUserAppsBtn->setProperty("type", "borderless");
// Refresh because Qt shows the scrollbar for one second then hides it, leaving a mark on the eInk screen
QTimer::singleShot(1750, this, SLOT(refreshScreenNative()));
jsonParseSuccess = parseJson();
showUserApps(false);
QFile stylesheetFile("/mnt/onboard/.adds/inkbox/eink.qss"); QFile stylesheetFile("/mnt/onboard/.adds/inkbox/eink.qss");
stylesheetFile.open(QFile::ReadOnly); stylesheetFile.open(QFile::ReadOnly);
this->setStyleSheet(stylesheetFile.readAll()); this->setStyleSheet(stylesheetFile.readAll());
@ -160,3 +174,174 @@ void apps::on_g2048LaunchBtn_clicked()
void apps::showToastNative(QString messageToDisplay) { void apps::showToastNative(QString messageToDisplay) {
emit showToast(messageToDisplay); emit showToast(messageToDisplay);
} }
/*
* This function opens and reads the main GUI user applications' JSON file, parses and verifies its content to avoid issues in the future.
*/
bool apps::parseJson() {
// If the path changes, it is also used statically in showUserApps()
jsonFile.setFileName("/mnt/onboard/onboard/.apps/apps.json");
bool jsonCheckSuccess = true;
if(jsonFile.exists() == false) {
log("GUI user applications' main JSON file is missing, creating it", className);
jsonCheckSuccess = false;
}
else {
jsonFile.open(QIODevice::ReadOnly | QIODevice::Text);
QString fileRead = jsonFile.readAll();
jsonFile.close();
jsonDocument = QJsonDocument::fromJson(fileRead.toUtf8());
if(jsonDocument.isNull() == true) {
log("GUI user applications' main JSON file is invalid", className);
jsonCheckSuccess = false;
}
else {
QJsonObject jsonObject = jsonDocument.object();
if(jsonObject["list"].isArray() == true) {
QJsonArray jsonArray = jsonObject["list"].toArray();
for(QJsonValueRef refJsonObject: jsonArray) {
if(refJsonObject.isObject()) {
QJsonObject jsonMainObject = refJsonObject.toObject();
if(jsonMainObject.size() == 9) {
if(!jsonMainObject["Name"].isString()) {
QString function = __func__; log(function + ": Invalid 'Name' type inside object", className);
jsonCheckSuccess = false;
}
if(!jsonMainObject["Author"].isString()) {
QString function = __func__; log(function + ": Invalid 'Author' type inside object", className);
jsonCheckSuccess = false;
}
if(!jsonMainObject["AuthorContact"].isString()) {
QString function = __func__; log(function + ": Invalid 'AuthorContact' type inside object", className);
jsonCheckSuccess = false;
}
if(!jsonMainObject["Version"].isString()) {
QString function = __func__; log(function + ": Invalid 'Version' type inside object", className);
jsonCheckSuccess = false;
}
if(!jsonMainObject["IconPath"].isString()) {
log("JSON: Invalid IconPath type inside object", className);
jsonCheckSuccess = false;
}
if(!jsonMainObject["ExecPath"].isString()) {
QString function = __func__; log(function + ": Invalid 'ExecPath' type inside object", className);
jsonCheckSuccess = false;
}
else {
if(jsonMainObject["ExecPath"].toString().contains("..")) {
// Possible security risk
showToastNative("ERROR: 'ExecPath' has invalid path");
QString function = __func__; log(function + ": 'ExecPath' contains \"..\"", className);
jsonCheckSuccess = false;
}
}
if(!jsonMainObject["Enabled"].isBool()) {
QString function = __func__; log(function + ": Invalid 'Enabled' type inside object", className);
jsonCheckSuccess = false;
}
if(!jsonMainObject["SupportedDevices"].isString()) {
QString function = __func__; log(function + ": Invalid 'SupportedDevices' type inside object", className);
jsonCheckSuccess = false;
}
if(!jsonMainObject["RequiredFeatures"].isArray()) {
QString function = __func__; log(function + ": Invalid 'RequiredFeatures' type inside object", className);
jsonCheckSuccess = false;
}
else {
QJsonArray jsonArray = jsonMainObject["RequiredFeatures"].toArray();
for(QJsonValueRef refJsonObject: jsonArray) {
// https://doc.qt.io/qt-5/qjsonvalue.html#toInt
if(!refJsonObject.isDouble()) {
QString function = __func__; log(function + ": Array from 'RequiredFeatures' contains a wrong type", className);
jsonCheckSuccess = false;
}
}
}
}
else {
QString function = __func__; QString message = function + ": An object inside list array has too many items (or not enough): ";
message.append(QString::number(jsonMainObject.size()));
log(message, className);
jsonCheckSuccess = false;
}
}
else {
QString function = __func__; log(function + ": List array has an item of other type than object", className);
jsonCheckSuccess = false;
}
}
}
}
}
return jsonCheckSuccess;
}
void apps::on_editUserAppsBtn_clicked()
{
if(userAppsSecondPage == false) {
userAppsSecondPage = true;
// Settings page
showUserApps(userAppsSecondPage);
emit showUserAppsEdit(userAppsSecondPage);
}
else {
userAppsSecondPage = false;
// Launch page
// It changed via updateJsonFileSlot, and now it writes it only once
jsonFile.open(QFile::WriteOnly | QFile::Text | QFile::Truncate);
jsonFile.write(jsonDocument.toJson());
jsonFile.flush();
jsonFile.close();
showUserApps(userAppsSecondPage);
emit showUserAppsEdit(userAppsSecondPage);
QTimer::singleShot(1000, this, SLOT(refreshScreenNative()));
}
}
void apps::showUserApps(bool showDisabledJson)
{
emit clearAppsLayout();
if(jsonParseSuccess == true) {
QString function = __func__; log(function + ": JSON is valid", className);
QJsonArray jsonListArray = jsonDocument.object()["list"].toArray();
for(QJsonValueRef refJsonObject: jsonListArray) {
QJsonObject appInfo = refJsonObject.toObject();
if(appInfo["Enabled"].toBool() == true or showDisabledJson == true) {
userapps * newUserApp = new userapps;
newUserApp->provideInfo(appInfo);
connect(this, SIGNAL(clearAppsLayout()), newUserApp, SLOT(deleteLater()));
connect(this, SIGNAL(showUserAppsEdit(bool)), newUserApp, SLOT(changePageEnabling(bool)));
connect(this, SIGNAL(updateJsonFileSignal(QJsonDocument)), newUserApp, SLOT(updateJsonFileSlotUA(QJsonDocument)));
connect(newUserApp, SIGNAL(updateJsonFileSignalUA(QJsonDocument)), this, SLOT(updateJsonFileSlot(QJsonDocument)));
newUserApp->jsonDocument = jsonDocument;
newUserApp->jsonFilePath = jsonFile.fileName();
ui->verticalLayout_4UserApps->addWidget(newUserApp);
}
}
}
else {
QString function = __func__; log(function + ": JSON is invalid", className);
emit showToast("ERROR: Failed to parse 'apps.json'");
}
}
void apps::updateJsonFileSlot(QJsonDocument jsonDocumentFunc)
{
jsonDocument = jsonDocumentFunc;
emit updateJsonFileSignal(jsonDocument);
}

14
apps.h
View file

@ -7,6 +7,8 @@
#include <koboxappsdialog.h> #include <koboxappsdialog.h>
#include <generaldialog.h> #include <generaldialog.h>
#include <QJsonDocument>
namespace Ui { namespace Ui {
class apps; class apps;
} }
@ -30,9 +32,13 @@ private slots:
void on_koboxAppsOpenButton_clicked(); void on_koboxAppsOpenButton_clicked();
void on_vncLaunchBtn_clicked(); void on_vncLaunchBtn_clicked();
void on_reversiLaunchBtn_clicked(); void on_reversiLaunchBtn_clicked();
void on_editUserAppsBtn_clicked();
void refreshScreenNative(); void refreshScreenNative();
void on_g2048LaunchBtn_clicked(); void on_g2048LaunchBtn_clicked();
void showToastNative(QString messageToDisplay); void showToastNative(QString messageToDisplay);
bool parseJson();
void showUserApps(bool showDisabledJson);
void updateJsonFileSlot(QJsonDocument jsonDocument);
private: private:
Ui::apps *ui; Ui::apps *ui;
@ -41,9 +47,17 @@ private:
koboxAppsDialog *koboxAppsDialogWindow; koboxAppsDialog *koboxAppsDialogWindow;
generalDialog *generalDialogWindow; generalDialog *generalDialogWindow;
QFile jsonFile;
QJsonDocument jsonDocument;
bool jsonParseSuccess = false;
bool userAppsSecondPage = false;
signals: signals:
void refreshScreen(); void refreshScreen();
void showToast(QString messageToDisplay); void showToast(QString messageToDisplay);
void updateJsonFileSignal(QJsonDocument jsonDocument);
void clearAppsLayout();
void showUserAppsEdit(bool show);
}; };
#endif // APPS_H #endif // APPS_H

611
apps.ui
View file

@ -6,8 +6,8 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>400</width> <width>430</width>
<height>321</height> <height>544</height>
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
@ -29,128 +29,59 @@
<item> <item>
<layout class="QGridLayout" name="gridLayout"> <layout class="QGridLayout" name="gridLayout">
<item row="4" column="0"> <item row="4" column="0">
<layout class="QGridLayout" name="gridLayout_2"> <widget class="QScrollArea" name="scrollArea">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Plain</enum>
</property>
<property name="lineWidth">
<number>0</number>
</property>
<property name="horizontalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOff</enum>
</property>
<property name="widgetResizable">
<bool>true</bool>
</property>
<widget class="QWidget" name="scrollAreaWidgetContents">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>428</width>
<height>542</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<property name="spacing">
<number>6</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin"> <property name="bottomMargin">
<number>0</number> <number>0</number>
</property> </property>
</layout> <item>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Apps</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item row="2" column="0">
<layout class="QGridLayout" name="gridLayout_3"> <layout class="QGridLayout" name="gridLayout_3">
<property name="bottomMargin"> <property name="rightMargin">
<number>0</number> <number>0</number>
</property> </property>
<item row="7" column="1"> <property name="bottomMargin">
<widget class="QPushButton" name="calculatorLaunchBtn"> <number>6</number>
<property name="font">
<font>
<family>Inter</family>
<weight>75</weight>
<bold>true</bold>
</font>
</property> </property>
<property name="text"> <property name="verticalSpacing">
<string>Launch</string> <number>10</number>
</property> </property>
</widget>
</item>
<item row="5" column="1">
<widget class="QPushButton" name="calendarLaunchBtn">
<property name="font">
<font>
<family>Inter</family>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Launch</string>
</property>
</widget>
</item>
<item row="8" column="0">
<widget class="QLabel" name="label_2">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Light Maps</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="scribbleLabel">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Scribble</string>
</property>
</widget>
</item>
<item row="6" column="1">
<widget class="QPushButton" name="savedWordsLaunchBtn">
<property name="font">
<font>
<family>Inter</family>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Launch</string>
</property>
</widget>
</item>
<item row="3" column="0"> <item row="3" column="0">
<widget class="QLabel" name="reversiLabel">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Reversi</string>
</property>
</widget>
</item>
<item row="6" column="0">
<widget class="QLabel" name="label_3">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Saved words</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_5"> <widget class="QLabel" name="label_5">
<property name="font"> <property name="font">
<font> <font>
@ -163,89 +94,7 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="5" column="0"> <item row="12" column="0">
<widget class="QLabel" name="calendarLabel">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Calendar</string>
</property>
</widget>
</item>
<item row="8" column="1">
<widget class="QPushButton" name="lightmapsLaunchBtn">
<property name="font">
<font>
<family>Inter</family>
<weight>75</weight>
<italic>false</italic>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Launch</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QPushButton" name="reversiLaunchBtn">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Launch</string>
</property>
</widget>
</item>
<item row="9" column="1">
<widget class="QPushButton" name="vncLaunchBtn">
<property name="font">
<font>
<family>Inter</family>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Launch</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QPushButton" name="koboxAppsOpenButton">
<property name="font">
<font>
<family>Inter</family>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Open</string>
</property>
</widget>
</item>
<item row="7" column="0">
<widget class="QLabel" name="label_4">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Calculator</string>
</property>
</widget>
</item>
<item row="9" column="0">
<widget class="QLabel" name="vncViewerLabel"> <widget class="QLabel" name="vncViewerLabel">
<property name="font"> <property name="font">
<font> <font>
@ -258,7 +107,7 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="1" column="1"> <item row="4" column="1">
<widget class="QPushButton" name="scribbleLaunchBtn"> <widget class="QPushButton" name="scribbleLaunchBtn">
<property name="font"> <property name="font">
<font> <font>
@ -273,8 +122,101 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="4" column="1"> <item row="9" column="0">
<widget class="QPushButton" name="g2048LaunchBtn"> <widget class="QLabel" name="label_3">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Saved words</string>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="scribbleLabel">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Scribble</string>
</property>
</widget>
</item>
<item row="10" column="0">
<widget class="QLabel" name="label_4">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Calculator</string>
</property>
</widget>
</item>
<item row="8" column="1">
<widget class="QPushButton" name="calendarLaunchBtn">
<property name="font">
<font>
<family>Inter</family>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Launch</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QPushButton" name="koboxAppsOpenButton">
<property name="font">
<font>
<family>Inter</family>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Open</string>
</property>
</widget>
</item>
<item row="11" column="0">
<widget class="QLabel" name="label_2">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Light Maps</string>
</property>
</widget>
</item>
<item row="6" column="0">
<widget class="QLabel" name="reversiLabel">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Reversi</string>
</property>
</widget>
</item>
<item row="6" column="1">
<widget class="QPushButton" name="reversiLaunchBtn">
<property name="font"> <property name="font">
<font> <font>
<weight>75</weight> <weight>75</weight>
@ -286,7 +228,35 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="4" column="0"> <item row="9" column="1">
<widget class="QPushButton" name="savedWordsLaunchBtn">
<property name="font">
<font>
<family>Inter</family>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Launch</string>
</property>
</widget>
</item>
<item row="10" column="1">
<widget class="QPushButton" name="calculatorLaunchBtn">
<property name="font">
<font>
<family>Inter</family>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Launch</string>
</property>
</widget>
</item>
<item row="7" column="0">
<widget class="QLabel" name="g2048Label"> <widget class="QLabel" name="g2048Label">
<property name="font"> <property name="font">
<font> <font>
@ -299,22 +269,78 @@
</property> </property>
</widget> </widget>
</item> </item>
</layout> <item row="7" column="1">
</item> <widget class="QPushButton" name="g2048LaunchBtn">
<item row="3" column="0"> <property name="font">
<spacer name="verticalSpacer"> <font>
<property name="orientation"> <weight>75</weight>
<enum>Qt::Vertical</enum> <bold>true</bold>
</font>
</property> </property>
<property name="sizeHint" stdset="0"> <property name="text">
<size> <string>Launch</string>
<width>20</width>
<height>40</height>
</size>
</property> </property>
</spacer> </widget>
</item> </item>
<item row="1" column="0"> <item row="12" column="1">
<widget class="QPushButton" name="vncLaunchBtn">
<property name="font">
<font>
<family>Inter</family>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Launch</string>
</property>
</widget>
</item>
<item row="11" column="1">
<widget class="QPushButton" name="lightmapsLaunchBtn">
<property name="font">
<font>
<family>Inter</family>
<weight>75</weight>
<italic>false</italic>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Launch</string>
</property>
</widget>
</item>
<item row="1" column="0" colspan="2">
<widget class="QLabel" name="label">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Built-in apps</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item row="8" column="0">
<widget class="QLabel" name="calendarLabel">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Calendar</string>
</property>
</widget>
</item>
<item row="2" column="0" colspan="2">
<widget class="Line" name="line"> <widget class="Line" name="line">
<property name="frameShadow"> <property name="frameShadow">
<enum>QFrame::Plain</enum> <enum>QFrame::Plain</enum>
@ -329,6 +355,161 @@
</item> </item>
</layout> </layout>
</item> </item>
<item>
<widget class="Line" name="line_3">
<property name="frameShadow">
<enum>QFrame::Plain</enum>
</property>
<property name="lineWidth">
<number>3</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="spacing">
<number>0</number>
</property>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="label_6">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>User apps</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>10</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="editUserAppsBtn">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>40</width>
<height>40</height>
</size>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset theme="://resources/edit.png">
<normaloff>://resources/edit.png</normaloff>://resources/edit.png</iconset>
</property>
<property name="iconSize">
<size>
<width>34</width>
<height>34</height>
</size>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<widget class="Line" name="line_2">
<property name="frameShadow">
<enum>QFrame::Plain</enum>
</property>
<property name="lineWidth">
<number>3</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_4UserApps">
<property name="spacing">
<number>10</number>
</property>
<property name="topMargin">
<number>2</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
</layout>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::MinimumExpanding</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</item>
</layout> </layout>
</widget> </widget>
<resources/> <resources/>

View file

@ -74,5 +74,6 @@
<file>resources/clock.png</file> <file>resources/clock.png</file>
<file>resources/eink-square-encfs.qss</file> <file>resources/eink-square-encfs.qss</file>
<file>resources/tzlist</file> <file>resources/tzlist</file>
<file>resources/edit.png</file>
</qresource> </qresource>
</RCC> </RCC>

View file

@ -122,6 +122,13 @@ namespace global {
namespace logger { namespace logger {
inline bool status; inline bool status;
} }
namespace userApps {
inline bool appCompatibilityDialog;
inline QString appCompatibilityText;
inline bool appCompatibilityLastContinueStatus = true; // This is for RequiredFeatures to show only one dialog if 'Cancel' is clicked.
inline bool appInfoDialog;
inline bool launchApp;
}
inline QString systemInfoText; inline QString systemInfoText;
inline bool forbidOpenSearchDialog; inline bool forbidOpenSearchDialog;
inline bool isN705 = false; inline bool isN705 = false;

View file

@ -13,6 +13,7 @@
#include <QStringListModel> #include <QStringListModel>
#include <QListView> #include <QListView>
#include <QDateTime> #include <QDateTime>
#include <QTextEdit>
generalDialog::generalDialog(QWidget *parent) : generalDialog::generalDialog(QWidget *parent) :
QDialog(parent), QDialog(parent),
@ -131,6 +132,7 @@ generalDialog::generalDialog(QWidget *parent) :
} }
else if(global::usbms::usbmsDialog == true) { else if(global::usbms::usbmsDialog == true) {
usbmsDialog = true; usbmsDialog = true;
ui->stackedWidget->setCurrentIndex(0);
ui->okBtn->setText("Connect"); ui->okBtn->setText("Connect");
ui->cancelBtn->setText("Not now"); ui->cancelBtn->setText("Not now");
ui->bodyLabel->setText("<font face='u001'>Do you want to connect your device to a computer to manage books</font><font face='Inter'>?</font>"); ui->bodyLabel->setText("<font face='u001'>Do you want to connect your device to a computer to manage books</font><font face='Inter'>?</font>");
@ -187,9 +189,29 @@ generalDialog::generalDialog(QWidget *parent) :
ui->bodyLabel->setText("<font face='u001'>New files have been found in 'encfs-dropbox'. Would you want to repack your encrypted storage</font><font face='Inter'>?</font>"); ui->bodyLabel->setText("<font face='u001'>New files have been found in 'encfs-dropbox'. Would you want to repack your encrypted storage</font><font face='Inter'>?</font>");
QTimer::singleShot(50, this, SLOT(adjust_size())); QTimer::singleShot(50, this, SLOT(adjust_size()));
} }
else if(global::userApps::appCompatibilityDialog == true) {
appCompabilityDialog = true;
global::userApps::appCompatibilityLastContinueStatus = true;
ui->okBtn->setText("Launch");
ui->cancelBtn->setText("Cancel");
ui->bodyLabel->setText(global::userApps::appCompatibilityText);
ui->headerLabel->setText("Compatibility warning");
QTimer::singleShot(50, this, SLOT(adjust_size()));
}
else if(global::userApps::appInfoDialog == true) {
appInfoDialog = true;
textwidgetWindow = new textwidget();
ui->headerLabel->setText("App info");
ui->stackedWidget->setCurrentIndex(1);
ui->mainStackedWidget->insertWidget(1, textwidgetWindow);
ui->mainStackedWidget->setCurrentIndex(1);
yIncrease = 1.8;
QTimer::singleShot(50, this, SLOT(increaseSize()));
}
else { else {
// We shouldn't be there ;) // We shouldn't be there ;)
; log("generalDialog launched without settings", className);
} }
// Centering dialog // Centering dialog
@ -248,6 +270,12 @@ void generalDialog::on_cancelBtn_clicked()
else if(global::encfs::repackDialog == true) { else if(global::encfs::repackDialog == true) {
global::encfs::repackDialog = false; global::encfs::repackDialog = false;
} }
else if(global::userApps::appCompatibilityDialog == true) {
global::userApps::launchApp = false;
global::userApps::appCompatibilityLastContinueStatus = false;
global::userApps::appCompatibilityText = "";
global::userApps::appCompatibilityDialog = false;
}
generalDialog::close(); generalDialog::close();
} }
} }
@ -507,6 +535,13 @@ void generalDialog::on_okBtn_clicked()
string_writeconfig("/external_root/run/encfs_repack", "true"); string_writeconfig("/external_root/run/encfs_repack", "true");
quit_restart(); quit_restart();
} }
else if(global::userApps::appCompatibilityDialog == true) {
global::userApps::launchApp = true;
global::userApps::appCompatibilityText = "";
global::userApps::appCompatibilityLastContinueStatus = true; // Not really necceserry, only if something fails horibly
global::userApps::appCompatibilityDialog = false;
generalDialog::close();
}
} }
void generalDialog::on_acceptBtn_clicked() void generalDialog::on_acceptBtn_clicked()
{ {
@ -533,6 +568,11 @@ void generalDialog::on_acceptBtn_clicked()
global::text::textBrowserDialog = false; global::text::textBrowserDialog = false;
} }
if(appInfoDialog == true) {
global::text::textBrowserContents = "";
global::userApps::appInfoDialog = false;
}
// We don't have any other option ;p // We don't have any other option ;p
generalDialog::close(); generalDialog::close();
} }
@ -552,6 +592,7 @@ void generalDialog::restartSearchDialog() {
} }
void generalDialog::setupKeyboardDialog() { void generalDialog::setupKeyboardDialog() {
ui->stackedWidget->setCurrentIndex(0);
keyboardDialog = true; keyboardDialog = true;
ui->stackedWidget->setVisible(true); ui->stackedWidget->setVisible(true);
if(global::keyboard::searchDialog == true) { if(global::keyboard::searchDialog == true) {
@ -732,3 +773,25 @@ void generalDialog::waitForGutenbergSearchDone() {
} }
} }
} }
void generalDialog::increaseSize()
{
log("Resizing generalDialog", className);
ui->topStackedWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
ui->mainStackedWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::MinimumExpanding);
ui->stackedWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
QRect screenGeometry = QGuiApplication::screens()[0]->geometry();
int wx = screenGeometry.width();
int x = wx - 25;
int y = this->height() * yIncrease;
this->setFixedWidth(x);
this->setFixedHeight(y);
ui->bodyLabel->sizePolicy().setVerticalPolicy(QSizePolicy::Expanding);
ui->bodyLabel->sizePolicy().setHorizontalPolicy(QSizePolicy::Expanding);
adjust_size();
}

View file

@ -32,6 +32,8 @@ public:
bool lowBatteryDialog = false; bool lowBatteryDialog = false;
bool usbmsDialog = false; bool usbmsDialog = false;
bool textBrowserDialog = false; bool textBrowserDialog = false;
bool appCompabilityDialog = false;
bool appInfoDialog = false;
bool resetKoboxDialog = false; bool resetKoboxDialog = false;
bool keyboardDialog = false; bool keyboardDialog = false;
bool keypadDialog = false; bool keypadDialog = false;
@ -49,6 +51,10 @@ public:
QString wifiPassphrase; QString wifiPassphrase;
void setupKeyboardDialog(); void setupKeyboardDialog();
void startVNC(QString server, QString password, QString port); void startVNC(QString server, QString password, QString port);
float yIncrease;
public slots:
void increaseSize();
private slots: private slots:
void on_cancelBtn_clicked(); void on_cancelBtn_clicked();

View file

@ -48,6 +48,7 @@ SOURCES += \
textwidget.cpp \ textwidget.cpp \
toast.cpp \ toast.cpp \
usbms_splash.cpp \ usbms_splash.cpp \
userapps.cpp \
virtualkeyboard.cpp \ virtualkeyboard.cpp \
virtualkeypad.cpp \ virtualkeypad.cpp \
wifidialog.cpp wifidialog.cpp
@ -77,6 +78,7 @@ HEADERS += \
textwidget.h \ textwidget.h \
toast.h \ toast.h \
usbms_splash.h \ usbms_splash.h \
userapps.h \
virtualkeyboard.h \ virtualkeyboard.h \
virtualkeypad.h \ virtualkeypad.h \
wifidialog.h wifidialog.h
@ -105,6 +107,7 @@ FORMS += \
textwidget.ui \ textwidget.ui \
toast.ui \ toast.ui \
usbms_splash.ui \ usbms_splash.ui \
userapps.ui \
virtualkeyboard.ui \ virtualkeyboard.ui \
virtualkeypad.ui \ virtualkeypad.ui \
wifidialog.ui wifidialog.ui

View file

@ -27,6 +27,101 @@
#include <QTextStream> #include <QTextStream>
#include <QRect> #include <QRect>
#include <QScreen> #include <QScreen>
#include <QDirIterator>
#include <QJsonDocument>
#include <QJsonParseError>
#include <QJsonObject>
#include <QJsonValue>
#include <QJsonArray>
void createMainJson()
{
QDirIterator appsDir("/mnt/onboard/onboard/.apps", QDirIterator::NoIteratorFlags);
QFile newJsonFile = QFile{"/mnt/onboard/onboard/.apps/apps.json"};
QJsonDocument newJsonDocument;
QJsonArray array;
while (appsDir.hasNext())
{
QDir dir(appsDir.next());
if(dir.exists() == true) {
if(dir.path().split("/").last().contains(".") == false) {
QFile jsonSmall = QFile{dir.path() + "/app.json"};
if(jsonSmall.exists() == true) {
jsonSmall.open(QIODevice::ReadOnly | QIODevice::Text);
QString jsonString = jsonSmall.readAll();
jsonSmall.close();
QJsonDocument jsonSmallDoc = QJsonDocument::fromJson(jsonString.toUtf8());
if(jsonSmallDoc["app"].isObject() == true) {
QJsonObject jsonSmallMainObj = jsonSmallDoc["app"].toObject();
array.append(jsonSmallMainObj);
}
else {
log("Small JSON user application file is missing main object 'app'", "main");
}
}
else {
QString message = "User applications folder does not contain any 'app.json' file: ";
message.append(jsonSmall.fileName());
log(message, "main");
}
}
}
}
// https://forum.qt.io/topic/104791/how-i-can-create-json-format-in-qt/5
QJsonObject root;
root["list"] = array;
newJsonDocument.setObject(root);
newJsonFile.open(QFile::WriteOnly | QFile::Text | QFile::Truncate);
newJsonFile.write(newJsonDocument.toJson());
newJsonFile.flush();
newJsonFile.close();
}
void createSmallJsonFiles()
{
QFile jsonFile = QFile{"/mnt/onboard/onboard/.apps/apps.json"};
jsonFile.open(QIODevice::ReadOnly | QIODevice::Text);
QString fileRead = jsonFile.readAll();
jsonFile.close();
QJsonDocument jsonDocument = QJsonDocument::fromJson(fileRead.toUtf8());
if(jsonDocument["list"].isArray() == true) {
QJsonArray jsonArray = jsonDocument["list"].toArray();
for(QJsonValueRef refJsonObject: jsonArray) {
QJsonObject jsonMainObject = refJsonObject.toObject();
QString appName = jsonMainObject["Name"].toString();
// This needs to be here and not at the beggining of this function because it is an iterator
QDirIterator appsDir("/mnt/onboard/onboard/.apps", QDirIterator::NoIteratorFlags);
while (appsDir.hasNext()) {
QDir dir(appsDir.next());
if(dir.exists() == true) {
if(dir.path().split("/").last().toLower().contains(appName.toLower()) == true) {
QJsonObject root;
root["app"] = refJsonObject.toObject();;
QJsonDocument newJsonDocument;
newJsonDocument.setObject(root);
QFile newSmallJson = QFile{dir.path() + "/" + "app.json"};
newSmallJson.open(QIODevice::ReadWrite);
QTextStream stream(&newSmallJson);
stream << newJsonDocument.toJson() << Qt::endl;
newSmallJson.flush();
newSmallJson.close();
}
}
}
}
}
}
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {

BIN
resources/edit.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

View file

@ -822,6 +822,10 @@ void settings::on_showSystemInfoBtn_clicked()
// Show a system info dialog // Show a system info dialog
log("Showing system info dialog", className); log("Showing system info dialog", className);
generalDialogWindow = new generalDialog(); generalDialogWindow = new generalDialog();
if(global::deviceID == "n306\n") {
generalDialogWindow->yIncrease = 2.6;
}
generalDialogWindow->increaseSize();
generalDialogWindow->setAttribute(Qt::WA_DeleteOnClose); generalDialogWindow->setAttribute(Qt::WA_DeleteOnClose);
} }

246
userapps.cpp Normal file
View file

@ -0,0 +1,246 @@
#include "userapps.h"
#include "ui_userapps.h"
#include "mainwindow.h"
#include "generaldialog.h"
userapps::userapps(QWidget *parent) :
QWidget(parent),
ui(new Ui::userapps)
{
ui->setupUi(this);
ui->launchBtn->setProperty("type", "borderless");
ui->launchBtn->setStyleSheet("background: lightGrey; font-size: 9pt; padding: 8px");
ui->launchBtn->setProperty("type", "borderless");
ui->iconBtn->setProperty("type", "borderless");
ui->infoBtn->setProperty("type", "borderless");
ui->statusBtn->setProperty("type", "borderless");
ui->statusBtn->setStyleSheet("background: lightGrey; font-size: 9pt; padding: 8px");
}
userapps::~userapps()
{
delete ui;
}
void userapps::provideInfo(QJsonObject jsonInfo)
{
QString name = jsonInfo["Name"].toString();
appName = name; // It is for searching for json entry while disabling / enabling
jsonObject = jsonInfo;
// Limit name size to avoid breaking the GUI
if(name.size() > 20)
{
// If someone wants to break the GUI, they will do it ¯\^-^/¯
name.remove(16, 100);
}
ui->appNameLabel->setText(name);
appDir.setPath("/mnt/onboard/onboard/.apps/" + name + "/" + name);
QFile iconPath = QFile{appDir.path() + "/" + jsonInfo["IconPath"].toString()};
if(iconPath.exists() == true) {
QIcon appIcon = QIcon(iconPath.fileName());
ui->iconBtn->setIconSize(QSize(40, 40));
ui->iconBtn->setIcon(appIcon);
}
else {
QString function = __func__;
QString message = ": Warning: Icon not found: ";
message.append(iconPath.fileName());
log(function + message, className);
}
execPath.setFileName("/" + jsonInfo["ExecPath"].toString());
userAppEnabled = jsonInfo["Enabled"].toBool();
if(userAppEnabled == true) {
ui->statusBtn->setText("Disable");
}
else {
ui->statusBtn->setText("Enable");
}
}
// This function is needed when we dont want to repaint all widgets, but only change the the page (when no changes to the main JSON file were applied)
void userapps::changePageEnabling(bool SecondPage)
{
if(SecondPage == true){
ui->stackedWidget->setCurrentIndex(1);
}
else {
ui->stackedWidget->setCurrentIndex(0);
}
}
void userapps::on_infoBtn_clicked()
{
log("Launching user application information dialog", className);
// https://stackoverflow.com/questions/28181627/how-to-convert-a-qjsonobject-to-qstring
QString jsonStringParsed = parseJsonShow(jsonObject);
global::text::textBrowserContents = jsonStringParsed;
global::userApps::appInfoDialog = true;
generalDialogWindow = new generalDialog();
generalDialogWindow->setAttribute(Qt::WA_DeleteOnClose);
generalDialogWindow->show();
}
void userapps::on_statusBtn_clicked()
{
ui->statusBtn->setEnabled(false);
// Here the text on this button is used as a bool. No need to create a new one
// Disable and Enable
if(userAppEnabled == false) {
userAppEnabled = true;
ui->statusBtn->setText("Disable");
}
else {
userAppEnabled = false;
ui->statusBtn->setText("Enable");
}
QJsonObject jsonRootObject = jsonDocument.object();
QJsonArray jsonArrayList = jsonRootObject["list"].toArray();
int arraySize = jsonArrayList.size();
for(int i = 0; i < arraySize; i++)
{
QJsonObject jsonObject = jsonArrayList.at(i).toObject();
QString entryName = jsonObject["Name"].toString();
if(entryName == appName)
{
jsonObject.insert("Enabled", QJsonValue(userAppEnabled));
QJsonArray sonArrayListNew = jsonDocument.object()["list"].toArray();
sonArrayListNew.replace(i, jsonObject);
jsonRootObject["list"] = sonArrayListNew;
jsonDocument.setObject(jsonRootObject);
emit updateJsonFileSignalUA(jsonDocument);
}
}
ui->statusBtn->setEnabled(true);
}
void userapps::updateJsonFileSlotUA(QJsonDocument jsonDocumentProvided)
{
jsonDocument = jsonDocumentProvided;
}
void userapps::on_launchBtn_clicked()
{
// Some command to execute script or binary at "ExecPath"
QString supportedDevices = jsonObject["SupportedDevices"].toString();
QString message = "Supported devices for this app: ";
message.append(supportedDevices);
log(message, className);
if(supportedDevices.contains("all") == false and supportedDevices.contains(global::deviceID.trimmed()) == false) {
log("Warning: User app does not support this device", className);
global::userApps::appCompatibilityDialog = true;
global::userApps::appCompatibilityText = "<font face='u001'>Your device is not compatible with this app.<br>Launch it anyway</font><font face='Inter'>?</font>";
generalDialogWindow = new generalDialog();
generalDialogWindow->setAttribute(Qt::WA_DeleteOnClose);
generalDialogWindow->exec();
}
else {
global::userApps::launchApp = true;
}
if(manageRequiredFeatures() == true) {
if(global::userApps::launchApp == true) {
global::userApps::launchApp = false;
QString message = "Launching user application at: ";
message.append(appDir.path() + execPath.fileName());
log(message, className);
QProcess process;
QStringList args;
args << appDir.path() << execPath.fileName();
process.startDetached("launch_user_application.sh", args);
qApp->quit();
}
}
}
bool userapps::manageRequiredFeatures()
{
QJsonArray jsonArray = jsonObject["RequiredFeatures"].toArray();
for(QJsonValueRef refJsonObject: jsonArray)
{
bool launchDialog = false;
int featureId = refJsonObject.toInt();
// Wi-Fi connection required
if(featureId == 0) {
global::userApps::appCompatibilityText = "<font face='u001'>This app needs Wi-Fi connection, launch anyway</font><font face='Inter'>?</font>";
launchDialog = true;
}
// Rooted kernel required
if(featureId == 1) {
global::userApps::appCompatibilityText = "<font face='u001'>This app needs a rooted kernel, launch anyway</font><font face='Inter'>?</font>";
launchDialog = true;
}
if(launchDialog == true)
{
global::userApps::appCompatibilityDialog = true;
generalDialogWindow = new generalDialog();
generalDialogWindow->setAttribute(Qt::WA_DeleteOnClose);
generalDialogWindow->exec();
}
if(global::userApps::appCompatibilityLastContinueStatus == false)
{
return false;
}
}
return true;
}
QString userapps::parseJsonShow(QJsonObject json)
{
QString mainString;
foreach(const QString& key, json.keys()) {
QJsonValue value = json.value(key);
QString appendString;
appendString.append("<b>");
appendString.append(key);
appendString.append("</b>: ");
if(value.isString()) {
appendString.append(value.toString());
}
else if(value.isBool()) {
appendString.append(QVariant(value.toBool()).toString());
}
else if(value.isArray()) {
QJsonArray array = value.toArray();
for(QJsonValueRef ref: array) {
int id = ref.toInt();
if(id == 0)
{
appendString.append("Wi-Fi connection");
} else if(id == 1)
{
appendString.append("Rooted kernel");
}
appendString.append(", ");
}
appendString.remove(appendString.size() - 2, 2);
}
appendString.append("<br>");
mainString.append(appendString);
}
return mainString;
}

58
userapps.h Normal file
View file

@ -0,0 +1,58 @@
#ifndef USERAPPS_H
#define USERAPPS_H
#include "generaldialog.h"
#include <QWidget>
#include <QJsonDocument>
#include <QJsonParseError>
#include <QJsonObject>
#include <QJsonValue>
#include <QJsonArray>
#include <QDir>
#include <QFile>
namespace Ui {
class userapps;
}
class userapps : public QWidget
{
Q_OBJECT
public:
QString className = this->metaObject()->className();
explicit userapps(QWidget * parent = nullptr);
~userapps();
QJsonDocument jsonDocument; // TODO: Optimize for RAM usage
QJsonObject jsonObject; // Needed for 'App info' button
QString jsonFilePath;
signals:
// This is needed, because user application have their own JSON file. If the user changes it too fast it won't read it and overwrite previous changes
void updateJsonFileSignalUA(QJsonDocument jsonDocument);
public slots:
void provideInfo(QJsonObject jsonObject);
void changePageEnabling(bool goThere);
void updateJsonFileSlotUA(QJsonDocument jsonDocument);
bool manageRequiredFeatures();
QString parseJsonShow(QJsonObject json);
private slots:
void on_infoBtn_clicked();
void on_statusBtn_clicked();
void on_launchBtn_clicked();
private:
Ui::userapps * ui;
generalDialog * generalDialogWindow;
QDir appDir;
QFile execPath;
bool userAppEnabled;
QString appName;
};
#endif // USERAPPS_H

221
userapps.ui Normal file
View file

@ -0,0 +1,221 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>userapps</class>
<widget class="QWidget" name="userapps">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>461</width>
<height>79</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QGridLayout" name="gridLayout_4">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<property name="spacing">
<number>0</number>
</property>
<item row="1" column="1">
<widget class="QStackedWidget" name="stackedWidget">
<property name="lineWidth">
<number>0</number>
</property>
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="pageLaunch">
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QPushButton" name="launchBtn">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Launch</string>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="page_2Edit">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QPushButton" name="infoBtn">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="eink.qrc">
<normaloff>:/resources/info.png</normaloff>:/resources/info.png</iconset>
</property>
<property name="iconSize">
<size>
<width>34</width>
<height>34</height>
</size>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="statusBtn">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Disable</string>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
<item row="1" column="0">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QPushButton" name="iconBtn">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>40</width>
<height>40</height>
</size>
</property>
<property name="text">
<string notr="true"/>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Preferred</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>5</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="appNameLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="lineWidth">
<number>0</number>
</property>
<property name="text">
<string>AppName</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Expanding</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>13</width>
<height>13</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</widget>
<resources>
<include location="eink.qrc"/>
</resources>
<connections/>
</ui>