mirror of
https://github.com/Chatterino/chatterino2.git
synced 2024-11-21 22:24:07 +01:00
now loads passwords for irc
This commit is contained in:
parent
b45a6eea22
commit
d33a8b1b3a
11 changed files with 116 additions and 83 deletions
|
@ -70,8 +70,6 @@ Application::Application(Settings &_settings, Paths &_paths)
|
||||||
this->fonts->fontChanged.connect(
|
this->fonts->fontChanged.connect(
|
||||||
[this]() { this->windows->layoutChannelViews(); });
|
[this]() { this->windows->layoutChannelViews(); });
|
||||||
|
|
||||||
Irc::getInstance().load();
|
|
||||||
|
|
||||||
this->twitch.server = this->twitch2;
|
this->twitch.server = this->twitch2;
|
||||||
this->twitch.pubsub = this->twitch2->pubsub;
|
this->twitch.pubsub = this->twitch2->pubsub;
|
||||||
}
|
}
|
||||||
|
@ -100,6 +98,7 @@ int Application::run(QApplication &qtApp)
|
||||||
assert(isAppInitialized);
|
assert(isAppInitialized);
|
||||||
|
|
||||||
this->twitch.server->connect();
|
this->twitch.server->connect();
|
||||||
|
Irc::getInstance().load();
|
||||||
|
|
||||||
this->windows->getMainWindow().show();
|
this->windows->getMainWindow().show();
|
||||||
|
|
||||||
|
|
|
@ -85,8 +85,8 @@ Credentials::Credentials()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Credentials::get(const QString &provider, const QString &name_,
|
void Credentials::get(const QString &provider, const QString &name_,
|
||||||
std::function<void(QString)> &&onLoaded)
|
std::function<void(const QString &)> &&onLoaded)
|
||||||
{
|
{
|
||||||
assertInGuiThread();
|
assertInGuiThread();
|
||||||
|
|
||||||
|
@ -100,16 +100,15 @@ QString Credentials::get(const QString &provider, const QString &name_,
|
||||||
QObject::connect(job, &QKeychain::Job::finished, qApp,
|
QObject::connect(job, &QKeychain::Job::finished, qApp,
|
||||||
[job, onLoaded = std::move(onLoaded)](auto) mutable {
|
[job, onLoaded = std::move(onLoaded)](auto) mutable {
|
||||||
onLoaded(job->textData());
|
onLoaded(job->textData());
|
||||||
});
|
},
|
||||||
|
Qt::DirectConnection);
|
||||||
job->start();
|
job->start();
|
||||||
|
|
||||||
return job->textData();
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
auto &instance = insecureInstance();
|
auto &instance = insecureInstance();
|
||||||
|
|
||||||
return instance.object().find(name).value().toString();
|
onLoaded(instance.object().find(name).value().toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,8 +10,8 @@ class Credentials
|
||||||
public:
|
public:
|
||||||
static Credentials &getInstance();
|
static Credentials &getInstance();
|
||||||
|
|
||||||
QString get(const QString &provider, const QString &name,
|
void get(const QString &provider, const QString &name,
|
||||||
std::function<void(QString)> &&onLoaded);
|
std::function<void(const QString &)> &&onLoaded);
|
||||||
void set(const QString &provider, const QString &name,
|
void set(const QString &provider, const QString &name,
|
||||||
const QString &credential);
|
const QString &credential);
|
||||||
void erase(const QString &provider, const QString &name);
|
void erase(const QString &provider, const QString &name);
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
#include "util/StandardItemHelper.hpp"
|
#include "util/StandardItemHelper.hpp"
|
||||||
|
|
||||||
#include <QSaveFile>
|
#include <QSaveFile>
|
||||||
|
#include <QtConcurrent>
|
||||||
|
|
||||||
namespace chatterino {
|
namespace chatterino {
|
||||||
|
|
||||||
|
@ -22,7 +23,7 @@ namespace {
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Model(QObject *parent)
|
Model(QObject *parent)
|
||||||
: SignalVectorModel<IrcServerData>(8, parent)
|
: SignalVectorModel<IrcServerData>(6, parent)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,8 +31,6 @@ namespace {
|
||||||
IrcServerData getItemFromRow(std::vector<QStandardItem *> &row,
|
IrcServerData getItemFromRow(std::vector<QStandardItem *> &row,
|
||||||
const IrcServerData &original)
|
const IrcServerData &original)
|
||||||
{
|
{
|
||||||
qDebug() << row[2]->data(Qt::CheckStateRole).toBool();
|
|
||||||
|
|
||||||
return IrcServerData{
|
return IrcServerData{
|
||||||
row[0]->data(Qt::EditRole).toString(), // host
|
row[0]->data(Qt::EditRole).toString(), // host
|
||||||
row[1]->data(Qt::EditRole).toInt(), // port
|
row[1]->data(Qt::EditRole).toInt(), // port
|
||||||
|
@ -39,7 +38,8 @@ namespace {
|
||||||
row[3]->data(Qt::EditRole).toString(), // user
|
row[3]->data(Qt::EditRole).toString(), // user
|
||||||
row[4]->data(Qt::EditRole).toString(), // nick
|
row[4]->data(Qt::EditRole).toString(), // nick
|
||||||
row[5]->data(Qt::EditRole).toString(), // real
|
row[5]->data(Qt::EditRole).toString(), // real
|
||||||
row[6]->data(Qt::EditRole).toString(), // password
|
original.password, // password
|
||||||
|
original.connectCommands, // connectCommands
|
||||||
original.id, // id
|
original.id, // id
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -54,30 +54,21 @@ namespace {
|
||||||
setStringItem(row[3], item.user);
|
setStringItem(row[3], item.user);
|
||||||
setStringItem(row[4], item.nick);
|
setStringItem(row[4], item.nick);
|
||||||
setStringItem(row[5], item.real);
|
setStringItem(row[5], item.real);
|
||||||
setStringItem(row[6], item.password);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
//inline QString escape(QString str)
|
inline QString escape(QString str)
|
||||||
//{
|
{
|
||||||
// return str.replace(":", "::");
|
return str.replace(":", "::");
|
||||||
//}
|
}
|
||||||
|
|
||||||
//inline QString getCredentialName(const IrcConnection_ &conn)
|
// This returns a unique id for every server which is understandeable in the systems credential manager.
|
||||||
//{
|
inline QString getCredentialName(const IrcServerData &data)
|
||||||
// //return escape(conn.host) + escape(conn.
|
{
|
||||||
//}
|
return escape(QString::number(data.id)) + ":" + escape(data.user) + "@" +
|
||||||
|
escape(data.host);
|
||||||
//QString IrcConnection_::getPassword()
|
}
|
||||||
//{
|
|
||||||
// return
|
|
||||||
//}
|
|
||||||
|
|
||||||
//void IrcConnection_::setPassword(const QString &str)
|
|
||||||
//{
|
|
||||||
// //Credentials::set("irc",
|
|
||||||
//}
|
|
||||||
|
|
||||||
Irc::Irc()
|
Irc::Irc()
|
||||||
{
|
{
|
||||||
|
@ -85,6 +76,7 @@ Irc::Irc()
|
||||||
// make sure only one id can only exist for one server
|
// make sure only one id can only exist for one server
|
||||||
assert(this->servers_.find(args.item.id) == this->servers_.end());
|
assert(this->servers_.find(args.item.id) == this->servers_.end());
|
||||||
|
|
||||||
|
// add new server
|
||||||
if (auto ab = this->abandonedChannels_.find(args.item.id);
|
if (auto ab = this->abandonedChannels_.find(args.item.id);
|
||||||
ab != this->abandonedChannels_.end())
|
ab != this->abandonedChannels_.end())
|
||||||
{
|
{
|
||||||
|
@ -107,6 +99,10 @@ Irc::Irc()
|
||||||
this->servers_.emplace(args.item.id,
|
this->servers_.emplace(args.item.id,
|
||||||
std::make_unique<IrcServer>(args.item));
|
std::make_unique<IrcServer>(args.item));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// store password
|
||||||
|
Credentials::getInstance().set("irc", getCredentialName(args.item),
|
||||||
|
args.item.password);
|
||||||
});
|
});
|
||||||
|
|
||||||
this->connections.itemRemoved.connect([this](auto &&args) {
|
this->connections.itemRemoved.connect([this](auto &&args) {
|
||||||
|
@ -125,6 +121,10 @@ Irc::Irc()
|
||||||
|
|
||||||
this->abandonedChannels_[args.item.id] = abandoned;
|
this->abandonedChannels_[args.item.id] = abandoned;
|
||||||
this->servers_.erase(server);
|
this->servers_.erase(server);
|
||||||
|
|
||||||
|
// delete password
|
||||||
|
Credentials::getInstance().erase("irc",
|
||||||
|
getCredentialName(args.item));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -146,7 +146,11 @@ ChannelPtr Irc::getOrAddChannel(int id, QString name)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return Channel::getEmpty();
|
auto channel = std::make_shared<IrcChannel>(name, nullptr);
|
||||||
|
|
||||||
|
this->abandonedChannels_[id].push_back(channel);
|
||||||
|
|
||||||
|
return std::move(channel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -187,7 +191,8 @@ void Irc::save()
|
||||||
obj.insert("username", conn.user);
|
obj.insert("username", conn.user);
|
||||||
obj.insert("nickname", conn.nick);
|
obj.insert("nickname", conn.nick);
|
||||||
obj.insert("realname", conn.real);
|
obj.insert("realname", conn.real);
|
||||||
//obj.insert("password", conn.password);
|
obj.insert("connectCommands",
|
||||||
|
QJsonArray::fromStringList(conn.connectCommands));
|
||||||
obj.insert("id", conn.id);
|
obj.insert("id", conn.id);
|
||||||
servers.append(obj);
|
servers.append(obj);
|
||||||
}
|
}
|
||||||
|
@ -210,29 +215,36 @@ void Irc::load()
|
||||||
QString config = configPath();
|
QString config = configPath();
|
||||||
QFile file(configPath());
|
QFile file(configPath());
|
||||||
file.open(QIODevice::ReadOnly);
|
file.open(QIODevice::ReadOnly);
|
||||||
auto doc = QJsonDocument::fromJson(file.readAll());
|
auto object = QJsonDocument::fromJson(file.readAll()).object();
|
||||||
|
|
||||||
auto object = doc.object();
|
// load servers
|
||||||
std::unordered_set<int> ids;
|
for (auto server : object.value("servers").toArray())
|
||||||
|
|
||||||
for (auto server : doc.object().value("servers").toArray())
|
|
||||||
{
|
{
|
||||||
auto obj = server.toObject();
|
auto obj = server.toObject();
|
||||||
IrcServerData conn;
|
IrcServerData data;
|
||||||
conn.host = obj.value("host").toString(conn.host);
|
data.host = obj.value("host").toString(data.host);
|
||||||
conn.port = obj.value("port").toInt(conn.port);
|
data.port = obj.value("port").toInt(data.port);
|
||||||
conn.ssl = obj.value("ssl").toBool(conn.ssl);
|
data.ssl = obj.value("ssl").toBool(data.ssl);
|
||||||
conn.user = obj.value("username").toString(conn.user);
|
data.user = obj.value("username").toString(data.user);
|
||||||
conn.nick = obj.value("nickname").toString(conn.nick);
|
data.nick = obj.value("nickname").toString(data.nick);
|
||||||
conn.real = obj.value("realname").toString(conn.real);
|
data.real = obj.value("realname").toString(data.real);
|
||||||
// conn.password = obj.value("password").toString(conn.password);
|
data.connectCommands =
|
||||||
conn.id = obj.value("id").toInt(conn.id);
|
obj.value("connectCommands").toVariant().toStringList();
|
||||||
|
data.id = obj.value("id").toInt(data.id);
|
||||||
|
|
||||||
// duplicate id's are not allowed :(
|
// duplicate id's are not allowed :(
|
||||||
if (ids.find(conn.id) == ids.end())
|
if (this->abandonedChannels_.find(data.id) ==
|
||||||
|
this->abandonedChannels_.end())
|
||||||
{
|
{
|
||||||
this->connections.appendItem(conn);
|
// insert element
|
||||||
ids.insert(conn.id);
|
this->abandonedChannels_[data.id];
|
||||||
|
|
||||||
|
Credentials::getInstance().get(
|
||||||
|
"irc", getCredentialName(data),
|
||||||
|
[=](const QString &password) mutable {
|
||||||
|
data.password = password;
|
||||||
|
this->connections.appendItem(data);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,7 @@ struct IrcServerData {
|
||||||
|
|
||||||
// IrcAuthType authType = Anonymous;
|
// IrcAuthType authType = Anonymous;
|
||||||
QString password;
|
QString password;
|
||||||
|
QStringList connectCommands;
|
||||||
|
|
||||||
int id;
|
int id;
|
||||||
};
|
};
|
||||||
|
@ -44,9 +45,6 @@ public:
|
||||||
|
|
||||||
int uniqueId();
|
int uniqueId();
|
||||||
|
|
||||||
signals:
|
|
||||||
void connectionUpdated(int id);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int currentId_{};
|
int currentId_{};
|
||||||
bool loaded_{};
|
bool loaded_{};
|
||||||
|
|
|
@ -14,6 +14,7 @@ public:
|
||||||
|
|
||||||
void sendMessage(const QString &message) override;
|
void sendMessage(const QString &message) override;
|
||||||
|
|
||||||
|
// server may be nullptr
|
||||||
IrcServer *server();
|
IrcServer *server();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -80,6 +80,11 @@ void IrcServer::onReadConnected(IrcConnection *connection)
|
||||||
|
|
||||||
std::lock_guard lock(this->channelMutex);
|
std::lock_guard lock(this->channelMutex);
|
||||||
|
|
||||||
|
for (auto &&command : this->data_->connectCommands)
|
||||||
|
{
|
||||||
|
connection->sendRaw(command + "\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
for (auto &&weak : this->channels)
|
for (auto &&weak : this->channels)
|
||||||
{
|
{
|
||||||
if (auto channel = weak.lock())
|
if (auto channel = weak.lock())
|
||||||
|
|
|
@ -621,7 +621,10 @@ void WindowManager::encodeChannel(IndirectChannel channel, QJsonObject &obj)
|
||||||
dynamic_cast<IrcChannel *>(channel.get().get()))
|
dynamic_cast<IrcChannel *>(channel.get().get()))
|
||||||
{
|
{
|
||||||
obj.insert("type", "irc");
|
obj.insert("type", "irc");
|
||||||
obj.insert("server", ircChannel->server()->id());
|
if (ircChannel->server())
|
||||||
|
{
|
||||||
|
obj.insert("server", ircChannel->server()->id());
|
||||||
|
}
|
||||||
obj.insert("channel", ircChannel->getName());
|
obj.insert("channel", ircChannel->getName());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,7 +26,13 @@ IrcConnectionEditor::IrcConnectionEditor(const IrcServerData &data, bool isAdd,
|
||||||
this->ui_->userNameLineEdit->setText(data.user);
|
this->ui_->userNameLineEdit->setText(data.user);
|
||||||
this->ui_->nickNameLineEdit->setText(data.nick);
|
this->ui_->nickNameLineEdit->setText(data.nick);
|
||||||
this->ui_->realNameLineEdit->setText(data.real);
|
this->ui_->realNameLineEdit->setText(data.real);
|
||||||
|
this->ui_->connectCommandsEditor->setPlainText(
|
||||||
|
data.connectCommands.join('\n'));
|
||||||
this->ui_->passwordLineEdit->setText(data.password);
|
this->ui_->passwordLineEdit->setText(data.password);
|
||||||
|
|
||||||
|
QFont font("Monospace");
|
||||||
|
font.setStyleHint(QFont::TypeWriter);
|
||||||
|
this->ui_->connectCommandsEditor->setFont(font);
|
||||||
} // namespace chatterino
|
} // namespace chatterino
|
||||||
|
|
||||||
IrcConnectionEditor::~IrcConnectionEditor()
|
IrcConnectionEditor::~IrcConnectionEditor()
|
||||||
|
@ -43,6 +49,8 @@ IrcServerData IrcConnectionEditor::data()
|
||||||
data.user = this->ui_->userNameLineEdit->text();
|
data.user = this->ui_->userNameLineEdit->text();
|
||||||
data.nick = this->ui_->nickNameLineEdit->text();
|
data.nick = this->ui_->nickNameLineEdit->text();
|
||||||
data.real = this->ui_->realNameLineEdit->text();
|
data.real = this->ui_->realNameLineEdit->text();
|
||||||
|
data.connectCommands =
|
||||||
|
this->ui_->connectCommandsEditor->toPlainText().split('\n');
|
||||||
data.password = this->ui_->passwordLineEdit->text();
|
data.password = this->ui_->passwordLineEdit->text();
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>264</width>
|
<width>264</width>
|
||||||
<height>350</height>
|
<height>414</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="windowTitle">
|
<property name="windowTitle">
|
||||||
|
@ -155,21 +155,38 @@
|
||||||
</item>
|
</item>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item row="10" column="0">
|
||||||
|
<widget class="QLabel" name="connectCommandsLabel">
|
||||||
|
<property name="text">
|
||||||
|
<string><html><head/><body><p>Send IRC commands</p><p>on connect:</p></body></html></string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="10" column="1">
|
||||||
|
<widget class="QPlainTextEdit" name="connectCommandsEditor">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>1</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="11" column="1">
|
||||||
|
<spacer name="verticalSpacer_3">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>20</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
|
||||||
<spacer name="verticalSpacer_3">
|
|
||||||
<property name="orientation">
|
|
||||||
<enum>Qt::Vertical</enum>
|
|
||||||
</property>
|
|
||||||
<property name="sizeHint" stdset="0">
|
|
||||||
<size>
|
|
||||||
<width>20</width>
|
|
||||||
<height>20</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
</spacer>
|
|
||||||
</item>
|
|
||||||
<item>
|
<item>
|
||||||
<widget class="QDialogButtonBox" name="buttonBox">
|
<widget class="QDialogButtonBox" name="buttonBox">
|
||||||
<property name="orientation">
|
<property name="orientation">
|
||||||
|
|
|
@ -130,7 +130,7 @@ SelectChannelDialog::SelectChannelDialog(QWidget *parent)
|
||||||
// irc
|
// irc
|
||||||
{
|
{
|
||||||
LayoutCreator<QWidget> obj(new QWidget());
|
LayoutCreator<QWidget> obj(new QWidget());
|
||||||
auto outerBox = obj.setLayoutType<QVBoxLayout>();
|
auto outerBox = obj.setLayoutType<QFormLayout>();
|
||||||
// outerBox.emplace<QLabel>("Connection:");
|
// outerBox.emplace<QLabel>("Connection:");
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -140,16 +140,11 @@ SelectChannelDialog::SelectChannelDialog(QWidget *parent)
|
||||||
view->setTitles({"host", "port", "ssl", "user", "nick", "real",
|
view->setTitles({"host", "port", "ssl", "user", "nick", "real",
|
||||||
"password", "login command"});
|
"password", "login command"});
|
||||||
view->getTableView()->horizontalHeader()->resizeSection(0, 140);
|
view->getTableView()->horizontalHeader()->resizeSection(0, 140);
|
||||||
view->getTableView()->horizontalHeader()->resizeSection(1, 40);
|
|
||||||
view->getTableView()->horizontalHeader()->resizeSection(2, 30);
|
|
||||||
//view->getTableView()->horizontalHeader()->setVisible(false);
|
|
||||||
|
|
||||||
view->getTableView()->horizontalHeader()->setSectionHidden(1, true);
|
view->getTableView()->horizontalHeader()->setSectionHidden(1, true);
|
||||||
view->getTableView()->horizontalHeader()->setSectionHidden(2, true);
|
view->getTableView()->horizontalHeader()->setSectionHidden(2, true);
|
||||||
view->getTableView()->horizontalHeader()->setSectionHidden(4, true);
|
view->getTableView()->horizontalHeader()->setSectionHidden(4, true);
|
||||||
view->getTableView()->horizontalHeader()->setSectionHidden(5, true);
|
view->getTableView()->horizontalHeader()->setSectionHidden(5, true);
|
||||||
view->getTableView()->horizontalHeader()->setSectionHidden(6, true);
|
|
||||||
view->getTableView()->horizontalHeader()->setSectionHidden(7, true);
|
|
||||||
|
|
||||||
view->addButtonPressed.connect([] {
|
view->addButtonPressed.connect([] {
|
||||||
auto unique = IrcServerData{};
|
auto unique = IrcServerData{};
|
||||||
|
@ -158,7 +153,7 @@ SelectChannelDialog::SelectChannelDialog(QWidget *parent)
|
||||||
auto editor = new IrcConnectionEditor(unique);
|
auto editor = new IrcConnectionEditor(unique);
|
||||||
if (editor->exec() == QDialog::Accepted)
|
if (editor->exec() == QDialog::Accepted)
|
||||||
{
|
{
|
||||||
Irc::getInstance().connections.appendItem(unique);
|
Irc::getInstance().connections.appendItem(editor->data());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -188,14 +183,10 @@ SelectChannelDialog::SelectChannelDialog(QWidget *parent)
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
outerBox->addWidget(view);
|
outerBox->addRow("Server:", view);
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
outerBox->addRow("Channel:", this->ui_.irc.channel = new QLineEdit);
|
||||||
auto box = outerBox.emplace<QHBoxLayout>().withoutMargin();
|
|
||||||
box.emplace<QLabel>("Channel:");
|
|
||||||
this->ui_.irc.channel = box.emplace<QLineEdit>().getElement();
|
|
||||||
}
|
|
||||||
|
|
||||||
auto tab = notebook->addPage(obj.getElement());
|
auto tab = notebook->addPage(obj.getElement());
|
||||||
tab->setCustomTitle("Irc");
|
tab->setCustomTitle("Irc");
|
||||||
|
|
Loading…
Reference in a new issue