diff --git a/include/championsearch.h b/include/championsearch.h new file mode 100644 index 0000000..b54d4fc --- /dev/null +++ b/include/championsearch.h @@ -0,0 +1,32 @@ +#ifndef CHAMPIONSEARCH_H +#define CHAMPIONSEARCH_H + +#include +#include "datadragon.h" +#include "champrow.h" + +namespace Ui { + class ChampionSearch; +} + +class ChampionSearch : public QDialog { + Q_OBJECT + +public: + explicit ChampionSearch(DataDragon* dd, QWidget *parent = nullptr); + ~ChampionSearch(); + + // does not return the same result on multiple calls! awsner needs to be deleted + ChampRow* getSearchResult(); + +private slots: + void searchChanged(QString); + +private: + void clear(); + + Ui::ChampionSearch *ui; + DataDragon* dd; +}; + +#endif // CHAMPIONSEARCH_H diff --git a/include/champrow.h b/include/champrow.h new file mode 100644 index 0000000..d3c2530 --- /dev/null +++ b/include/champrow.h @@ -0,0 +1,20 @@ +#pragma once + +#include + +#include "datadragon.h" + +class ChampRow : public QListWidgetItem { + +public: + explicit ChampRow(QListWidget *parent = nullptr); + ~ChampRow(); + + void setChamp(const std::string& name, uint32_t id, QPixmap icon); + QString getChamp() const; + uint32_t getChampID() const; + QPixmap getIcon(); +private: + uint32_t champid = -1; + QPixmap icon; +}; diff --git a/include/config.h b/include/config.h index 4bb9f18..58e494a 100644 --- a/include/config.h +++ b/include/config.h @@ -9,7 +9,7 @@ public: StageConfig(const QJsonObject&); operator QJsonObject() const; - std::string champ; + std::vector champs; bool enabled; }; diff --git a/include/datadragon.h b/include/datadragon.h index 5a50d61..0b01504 100644 --- a/include/datadragon.h +++ b/include/datadragon.h @@ -49,6 +49,7 @@ public: void getImageAsnyc(const std::string& champid, notifyImgfunc_t func, ImageType imgtype = ImageType::SQUARE); // might block until champ data is available const ChampData& getBestMatchingChamp(const std::string& name, int* count = nullptr); + std::vector getMatchingChamp(const std::string& name, uint32_t limit = 25); static const ChampData EMPTYCHAMP; protected: diff --git a/include/lolautoaccept.h b/include/lolautoaccept.h index 499b4a4..aa8b37b 100644 --- a/include/lolautoaccept.h +++ b/include/lolautoaccept.h @@ -11,9 +11,8 @@ protected: Stage(); virtual ~Stage(); - uint32_t champid = 0; // not every stage has a champ + std::vector champids; // not every stage has a champ bool enabled = false; - virtual void action(LolAutoAccept& lolaa); }; std::vector stages; @@ -37,7 +36,7 @@ public: LolAutoAccept(); ~LolAutoAccept(); - void setChamp(uint32_t champ, State s); + void setChamps(std::vector champs, State s); void setEnabled(bool b, State s); bool init(); // returns true on success diff --git a/include/stagesettings.h b/include/stagesettings.h index 29d63b1..cbea8bf 100644 --- a/include/stagesettings.h +++ b/include/stagesettings.h @@ -14,7 +14,6 @@ class StageSettings : public QWidget { Q_PROPERTY(QString name READ getName WRITE setName) Q_PROPERTY(bool state READ getState WRITE setState NOTIFY toggled) - Q_PROPERTY(QString champion READ getChampion WRITE setChampion NOTIFY championChanged) public: explicit StageSettings(QWidget *parent = nullptr); @@ -26,29 +25,37 @@ public: bool getState() const; void setState(bool); - QString getChampion() const; - void setChampion(const QString& str); + struct SelectedChamp { + SelectedChamp(std::string name, uint32_t id); + std::string name; + uint32_t id; + }; + + std::vector getChampions() const; + void setChampions(const std::vector& champs); void setDataDragon(DataDragon* dd); - - void resizeEvent(QResizeEvent *event) override; + void addChamp(const std::string& champname, uint32_t id, QPixmap icon); private slots: - void championChangedinternal(const QString& str); void toggledinternal(int state); + void addChamp(); + void removeChamp(); + void moveUp(); + void moveDown(); signals: void toggled(bool); - void championChanged(const QString&); + void championChanged(int position, const QString&); private: - void rescaleImage(); - void applyChampion(const DataDragon::ChampData& cd); + // delete all items + void resolveAndAddChamp(const std::string& name); + void clear(); + void updateEnabled(); Ui::StageSettings *ui; DataDragon* dd = nullptr; - int currentdisplayedChampKey = -1; - QPixmap img; }; #endif // STAGESETTINGS_H diff --git a/lolautoaccept.pro b/lolautoaccept.pro index 48830d4..6ac075d 100644 --- a/lolautoaccept.pro +++ b/lolautoaccept.pro @@ -24,6 +24,8 @@ defineReplace(prependAll) { SOURCES += \ src/arg.cpp \ + src/championsearch.cpp \ + src/champrow.cpp \ src/clientaccess.cpp \ src/clientapi_json.cpp \ src/clientapi.cpp \ @@ -44,6 +46,8 @@ SOURCES += \ HEADERS += \ include/arg.h \ + include/championsearch.h \ + include/champrow.h \ include/clientaccess.h \ include/clientapi.h \ include/config.h \ @@ -65,6 +69,7 @@ UI_DIR = ui/ OBJECTS_DIR = build/ FORMS += \ + ui/championsearch.ui \ ui/mainwindow.ui \ ui/stagesettings.ui diff --git a/src/championsearch.cpp b/src/championsearch.cpp new file mode 100644 index 0000000..1add0bc --- /dev/null +++ b/src/championsearch.cpp @@ -0,0 +1,40 @@ +#include "championsearch.h" +#include "ui_championsearch.h" + +#include + +ChampionSearch::ChampionSearch(DataDragon* dd, QWidget *parent) : QDialog(parent), ui(new Ui::ChampionSearch), dd(dd) { + ui->setupUi(this); +} + +ChampionSearch::~ChampionSearch() { + delete ui; +} + +ChampRow* ChampionSearch::getSearchResult() { + int row = ui->championList->currentRow(); + return (ChampRow*) ui->championList->takeItem(row); +} + +void ChampionSearch::searchChanged(QString str) { + Log::info << "champion search: " << str.toStdString(); + + auto champs = dd->getMatchingChamp(str.toStdString()); + Log::info << "found " << champs.size() << " champs"; + + clear(); + + for(auto it : champs) { + dd->getImageAsnyc(it->id, [this, it](QPixmap img) { + auto cr = new ChampRow(); + cr->setChamp(it->name, it->key, img); + ui->championList->addItem(cr); + }); + } +} + +void ChampionSearch::clear() { + while(ui->championList->count()) { + delete ui->championList->takeItem(0); + } +} \ No newline at end of file diff --git a/src/champrow.cpp b/src/champrow.cpp new file mode 100644 index 0000000..a3d7d90 --- /dev/null +++ b/src/champrow.cpp @@ -0,0 +1,26 @@ +#include "champrow.h" + +ChampRow::ChampRow(QListWidget* parent) : QListWidgetItem(parent) { +} + +ChampRow::~ChampRow() { +} + +void ChampRow::setChamp(const std::string& name, uint32_t id, QPixmap icon) { + setText(QString::fromStdString(name)); + champid = id; + this->icon = icon; + setIcon(QIcon(icon)); +} + +QString ChampRow::getChamp() const { + return text(); +} + +uint32_t ChampRow::getChampID() const { + return champid; +} + +QPixmap ChampRow::getIcon() { + return icon; +} diff --git a/src/config.cpp b/src/config.cpp index 43b72cc..7c6f1fa 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -3,6 +3,7 @@ #include "files.h" #include #include +#include #include @@ -11,14 +12,34 @@ Config::StageConfig::StageConfig() : enabled(false) {} Config::StageConfig::StageConfig(const QJsonObject& j) { - champ = getValue(j, "champ", ""); + if(j["champ"].isString()) { + champs.push_back(getValue(j, "champ", "")); + } + if(j["champs"].isArray()) { + QJsonArray jchamps = j["champs"].toArray(); + for(auto jchamp : jchamps) { + if(jchamp.isString()) { + QString jchampstr = jchamp.toString(); + if(!jchampstr.isEmpty()) { + champs.push_back(jchampstr.toStdString()); + } + } + } + } enabled = getValue(j, "enabled", false); } Config::StageConfig::operator QJsonObject() const { QJsonObject out; - out.insert("champ", QString::fromStdString(champ)); + QJsonArray jchamps; + for(const std::string& champ : champs) { + if(!champ.empty()) { + jchamps.push_back(QString::fromStdString(champ)); + } + } + + out.insert("champs", jchamps); out.insert("enabled", enabled); return out; diff --git a/src/datadragon.cpp b/src/datadragon.cpp index cb35b7d..f0db6e9 100644 --- a/src/datadragon.cpp +++ b/src/datadragon.cpp @@ -9,7 +9,7 @@ #include #include -#include // std::max +#include // std::max, champ matching #include "json.h" @@ -162,6 +162,51 @@ const DataDragon::ChampData& DataDragon::getBestMatchingChamp(const std::string& return EMPTYCHAMP; } +std::vector DataDragon::getMatchingChamp(const std::string& name, uint32_t limit) { + if(limit == 0) return {}; + if(name.empty()) return {}; + + getChamps(); + std::string lower = toLower(name); + + std::vector matches; + matches.resize(champs.size()); + std::transform(champs.begin(), champs.end(), std::insert_iterator(matches, matches.begin()), std::bind(&matchChamp, std::placeholders::_1, lower)); + + { + Log::Entry e = Log::info << "matches content: "; + for(size_t m : matches) { + e << m << " "; + } + } + + // find smallest element + std::vector matches_sort = matches; + std::nth_element(matches_sort.begin(), matches_sort.begin(), matches_sort.end(), [](size_t a, size_t b) { return b < a; }); + size_t border = matches_sort.at(0); + + { + Log::Entry e = Log::info << "matches_sort content: "; + for(size_t m : matches_sort) { + e << m << " "; + } + } + + Log::trace << "border: " << border; + + std::vector out; + out.reserve(limit); + + // take all with a match higher than that + for(uint32_t i = 0; i < matches.size() && out.size() < limit; ++i) { + if(matches[i] >= border) { + out.push_back(&champs[i]); + } + } + + return out; +} + std::string DataDragon::getImageUrl(const std::string& champid, ImageType type) { switch(type) { case ImageType::SQUARE: { diff --git a/src/lolautoaccept.cpp b/src/lolautoaccept.cpp index 313b646..eb1699a 100644 --- a/src/lolautoaccept.cpp +++ b/src/lolautoaccept.cpp @@ -7,8 +7,6 @@ LolAutoAccept::Stage::Stage() {} LolAutoAccept::Stage::~Stage() {} -void LolAutoAccept::Stage::action(LolAutoAccept& lolaa) {} - LolAutoAccept::LolAutoAccept() { stages.reserve(4); stages.push_back(new Stage()); // accept @@ -25,14 +23,14 @@ LolAutoAccept::~LolAutoAccept() { } } -void LolAutoAccept::setChamp(uint32_t champ, State s) { +void LolAutoAccept::setChamps(std::vector champs, State s) { if(s == State::LOBBY || s >= State::GAME) { - Log::warn << "setChamp() called on invalid State"; + Log::warn << "setChamps() called on invalid State"; return; } - stages.at((int) s)->champid = champ; - Log::info << "set state: " << (int) s << " to " << champ; + stages.at((int) s)->champids = champs; + Log::info << "set champs on state: " << (int) s; } void LolAutoAccept::setEnabled(bool b, State s) { @@ -132,7 +130,7 @@ void LolAutoAccept::prepickPhase(const ownactions_t& ownactions) { Log::info << "pre-pick action anvailable: " << it.id << " champid: " << it.championID; if(it.championID == 0) { // try to prepick a champion - uint32_t champid = stages.at((int) State::PREPICK)->champid; + uint32_t champid = stages.at((int) State::PREPICK)->champids.at(0); // TODO: do on multi selection Log::info << "try to prepick champ: " << champid; clientapi->setChampSelectAction(it.id, champid, false); break; @@ -149,7 +147,7 @@ void LolAutoAccept::banPhase(const ownactions_t& ownactions) { Log::info << "ban action anvailable: " << it.id << " champid: " << it.championID; if(it.championID == 0) { // try to ban a champion - uint32_t champid = stages.at((int) State::BAN)->champid; + uint32_t champid = stages.at((int) State::BAN)->champids.at(0); // TODO Log::info << "try to ban champ: " << champid; clientapi->setChampSelectAction(it.id, champid, true); return; @@ -164,7 +162,7 @@ void LolAutoAccept::pickPhase(const ownactions_t& ownactions) { for(auto it : ownactions) { if(it.type == ClientAPI::ChampSelectActionType::PICK) { Log::info << "pick action anvailable: " << it.id << " champid: " << it.championID; - uint32_t champid = stages.at((int) State::PICK)->champid; + uint32_t champid = stages.at((int) State::PICK)->champids.at(0); // TODO if(it.championID != champid || !it.completed) { // try to prepick a champion Log::info << "try to pick champ: " << champid; diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 5643a4c..be7399f 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -5,7 +5,7 @@ static void applySetting(const Config::StageConfig& sc, StageSettings* ss) { ss->setState(sc.enabled); - ss->setChampion(QString::fromStdString(sc.champ)); + ss->setChampions(sc.champs); } MainWindow::MainWindow(LolAutoAccept& lolaa, QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow), lolaa(lolaa), dd(QLocale().name().toStdString()) { @@ -61,6 +61,16 @@ void MainWindow::aatoggled(bool state) { conf.getConfig().enabledAutoAccept = state; } +static std::vector toChampionNameList(const std::vector& scs) { + std::vector out; + + for(const StageSettings::SelectedChamp& sc : scs) { + out.push_back(sc.name); + } + + return out; +} + void MainWindow::pptoggled(bool state) { Log::info << "enablePrePick checkbox toggled " << state; lolaa.setEnabled(state, LolAutoAccept::State::PREPICK); @@ -69,8 +79,8 @@ void MainWindow::pptoggled(bool state) { void MainWindow::ppedited(const QString& b) { Log::info << "prepick edited: " << b.toStdString(); - lolaa.setChamp(dd.getBestMatchingChamp(b.toStdString()).key, LolAutoAccept::State::PREPICK); - conf.getConfig().prepick.champ = b.toStdString(); + //lolaa.setChamps(dd.getBestMatchingChamp(b.toStdString()).key, LolAutoAccept::State::PREPICK); + conf.getConfig().prepick.champs = toChampionNameList(ui->prepickstage->getChampions()); } void MainWindow::bantoggled(bool state) { @@ -81,8 +91,8 @@ void MainWindow::bantoggled(bool state) { void MainWindow::banedited(const QString& b) { Log::info << "ban edited: " << b.toStdString(); - lolaa.setChamp(dd.getBestMatchingChamp(b.toStdString()).key, LolAutoAccept::State::BAN); - conf.getConfig().ban.champ = b.toStdString(); + //lolaa.setChamp(dd.getBestMatchingChamp(b.toStdString()).key, LolAutoAccept::State::BAN); + conf.getConfig().ban.champs = toChampionNameList(ui->banstage->getChampions()); } void MainWindow::picktoggled(bool state) { @@ -93,6 +103,6 @@ void MainWindow::picktoggled(bool state) { void MainWindow::pickedited(const QString& b) { Log::info << "pick edited: " << b.toStdString(); - lolaa.setChamp(dd.getBestMatchingChamp(b.toStdString()).key, LolAutoAccept::State::PICK); - conf.getConfig().pick.champ = b.toStdString(); + //lolaa.setChamp(dd.getBestMatchingChamp(b.toStdString()).key, LolAutoAccept::State::PICK); + conf.getConfig().pick.champs = toChampionNameList(ui->pickstage->getChampions()); } diff --git a/src/stagesettings.cpp b/src/stagesettings.cpp index af1b92c..ca2105d 100644 --- a/src/stagesettings.cpp +++ b/src/stagesettings.cpp @@ -5,11 +5,13 @@ #include +#include "championsearch.h" +#include "champrow.h" + StageSettings::StageSettings(QWidget *parent) : QWidget(parent), ui(new Ui::StageSettings) { ui->setupUi(this); setMinimumSize(ui->gridLayout->minimumSize()); - rescaleImage(); } StageSettings::~StageSettings() { @@ -32,76 +34,112 @@ bool StageSettings::getState() const { void StageSettings::setState(bool b) { ui->checkBox->setCheckState(b ? Qt::CheckState::Checked : Qt::CheckState::Unchecked); emit toggled(b); + updateEnabled(); } -QString StageSettings::getChampion() const { - return ""; // ui->lineEdit->text(); +StageSettings::SelectedChamp::SelectedChamp(std::string name, uint32_t id) : name(name), id(id) {} + +std::vector StageSettings::getChampions() const { + std::vector out; + out.reserve(ui->championList->count()); + for(int32_t i = 0; i < ui->championList->count(); ++i) { + ChampRow* cr = (ChampRow*) ui->championList->item(i); + out.emplace_back(cr->getChamp().toStdString(), cr->getChampID()); + } + return out; } -void StageSettings::setChampion(const QString& str) { - // ui->lineEdit->setText(str); - championChangedinternal(str); +void StageSettings::setChampions(const std::vector& champs) { + clear(); + + // add new champs + for(const std::string& champ : champs) { + auto champr = new ChampRow(); + ui->championList->addItem(champr); + + champr->setChamp(champ, 0, QPixmap("/home/yannis/.cache/lolautoaccept/square/Orianna.png")); // TODO + } } void StageSettings::setDataDragon(DataDragon* dd_) { dd = dd_; } -void StageSettings::resizeEvent([[maybe_unused]] QResizeEvent* event) { - rescaleImage(); -} +void StageSettings::addChamp(const std::string& champname, uint32_t id, QPixmap icon) { + auto champr = new ChampRow(); + ui->championList->addItem(champr); -void StageSettings::championChangedinternal(const QString& str) { - if(dd) { - int count = 0; - const DataDragon::ChampData& cd = dd->getBestMatchingChamp(str.toStdString(), &count); - if(cd.key != currentdisplayedChampKey) { - applyChampion(cd); - } - //ui->champcount->setText(tr("Champions matched: %1").arg(QString::fromStdString(std::to_string(count)))); - } else { - emit championChanged(str); - } + champr->setChamp(champname, id, icon); // TODO } void StageSettings::toggledinternal(int state) { emit toggled(state == Qt::CheckState::Checked); + updateEnabled(); } -void StageSettings::rescaleImage() { - if(img.isNull()) { - //ui->champImg->setPixmap(QPixmap()); - return; - } +void StageSettings::addChamp() { + Log::trace << "add champ"; + // TODO: popup with champion search - //auto qimg = QImage((uchar*) img.data, img.cols, img.rows, img.step, QImage::Format_RGB888); - // auto p = QPixmap::fromImage(qimg); - - // get label dimensions - //int w = ui->champImg->width(); - //int h = ui->champImg->height(); - - // set a scaled pixmap to a w x h window keeping its aspect ratio - //ui->champImg->setPixmap(p.scaled(w, h, Qt::KeepAspectRatio)); // -} - -void StageSettings::applyChampion(const DataDragon::ChampData& cd) { - dd->getImageAsnyc(cd.id, [this, cd](QPixmap img) { - this->img = img; - currentdisplayedChampKey = cd.key; - rescaleImage(); - - QIcon icon(this->img); - ui->championList->addItem(new QListWidgetItem(icon, "Champ", nullptr, 0)); - - emit championChanged(QString::fromStdString(cd.name)); - }); - - if(cd.key >= 0) { - #define QS(A) arg(QString::fromStdString(A)) - //ui->champImg->setToolTip(tr("Champion: %1\nType: %2\nTitle: %3\nID: %4").QS(cd.name).QS(cd.partype).QS(cd.title).arg(cd.key)); - #undef QS + ChampionSearch cs(dd, this); + int res = cs.exec(); + Log::info << "championsearch result: " << res; + if(res == QDialog::Accepted) { + auto cr = cs.getSearchResult(); + if(cr) { + ui->championList->addItem(cr); + // TODO: emit value changed something + } } else { - //ui->champImg->setToolTip(""); + // not accepted } } + +void StageSettings::removeChamp() { + Log::trace << "remove champ"; + int row = ui->championList->currentRow(); // what if no row is selected? + delete ui->championList->takeItem(row); +} + +void StageSettings::moveUp() { + Log::trace << "move up"; + if(ui->championList->selectedItems().isEmpty()) return; + + auto selection = ui->championList->selectedItems().at(0); + ui->championList->removeItemWidget(selection); + ui->championList->insertItem(0, selection); +} + +void StageSettings::moveDown() { + Log::trace << "move down"; +} + +void StageSettings::resolveAndAddChamp(const std::string& name) { + if(dd) { + int count = 0; + auto cd = dd->getBestMatchingChamp(name, &count); + + dd->getImageAsnyc(cd.id, [this, name, cd](QPixmap img) { + addChamp(name, cd.key, img); + emit championChanged(0, QString::fromStdString(cd.name)); + }); + } else { + Log::error << __PRETTY_FUNCTION__ << " Datadragon not set!"; + } +} + +void StageSettings::clear() { + while(ui->championList->count()) { + delete ui->championList->takeItem(0); + } +} + +void StageSettings::updateEnabled() { + bool active = ui->checkBox->isChecked(); + + ui->championList->setEnabled(active); + ui->addChampion->setEnabled(active); + ui->moveup->setEnabled(active); + ui->movedown->setEnabled(active); + ui->removeChampion->setEnabled(active); +} diff --git a/ui/championsearch.ui b/ui/championsearch.ui new file mode 100644 index 0000000..6c469f6 --- /dev/null +++ b/ui/championsearch.ui @@ -0,0 +1,96 @@ + + + ChampionSearch + + + + 0 + 0 + 400 + 300 + + + + Champion Search + + + + + + Champion: + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + + + + buttonBox + accepted() + ChampionSearch + accept() + + + 304 + 290 + + + 157 + 274 + + + + + buttonBox + rejected() + ChampionSearch + reject() + + + 372 + 290 + + + 286 + 274 + + + + + searchbox + textChanged(QString) + ChampionSearch + searchChanged(QString) + + + 218 + 21 + + + 399 + 28 + + + + + + searchChanged(QString) + + diff --git a/ui/stagesettings.ui b/ui/stagesettings.ui index 936a945..243ec9c 100644 --- a/ui/stagesettings.ui +++ b/ui/stagesettings.ui @@ -65,6 +65,12 @@ 150 + + false + + + QAbstractItemView::InternalMove + @@ -116,8 +122,8 @@ toggledinternal(int) - 79 - 109 + 100 + 57 20 @@ -125,11 +131,79 @@ + + addChampion + clicked() + StageSettings + addChamp() + + + 643 + 209 + + + 757 + 291 + + + + + moveup + clicked() + StageSettings + moveUp() + + + 683 + 371 + + + 758 + 401 + + + + + movedown + clicked() + StageSettings + moveDown() + + + 702 + 526 + + + 758 + 536 + + + + + removeChampion + clicked() + StageSettings + removeChamp() + + + 717 + 691 + + + 758 + 697 + + + championChanged(QString) toggled(bool) championChangedinternal(QString) toggledinternal(int) + addChamp() + removeChamp() + moveUp() + moveDown()