simple button search

This commit is contained in:
mrbesen 2022-09-21 00:33:41 +02:00
parent 69a5be6608
commit e4d6a15682
Signed by: MrBesen
GPG Key ID: 596B2350DCD67504
12 changed files with 406 additions and 13 deletions

39
include/buttonsearch.h Normal file
View File

@ -0,0 +1,39 @@
#pragma once
#include <QDialog>
#include "qxtglobalshortcut.h"
#include "config.h"
namespace Ui {
class ButtonSearch;
}
class ButtonSearch : public QDialog {
Q_OBJECT
public:
explicit ButtonSearch(const Config& config, QWidget *parent = nullptr);
ButtonSearch(const ButtonSearch&) = delete; // no copy
~ButtonSearch();
protected:
void buttonPressed(int i);
private slots:
void closeSearch();
void selectionDown();
void selectionUp();
private:
const Config& config;
Ui::ButtonSearch *ui;
static const uint8_t NUMCOUNT = 46;
QxtGlobalShortcut* esc;
QxtGlobalShortcut* enter;
QxtGlobalShortcut* up;
QxtGlobalShortcut* down;
QxtGlobalShortcut* nums[NUMCOUNT];
};

View File

@ -11,6 +11,7 @@ public:
void load();
void save();
void assignIDs();
struct AudioConfig {
std::vector<std::string> devices;
@ -29,6 +30,7 @@ public:
struct ButtonConfig {
std::string name;
std::string key;
uint32_t id;
uint8_t width = DEFAULTWIDTH; // default is 6
std::vector<SampleConfig> samples;
@ -43,6 +45,7 @@ public:
std::string left;
std::string right;
std::string play;
std::string search;
std::string stop;
};
@ -54,6 +57,7 @@ public:
ShortcutConfig shortcuts;
} rootConfig;
const ButtonConfig* getButtonByID(uint32_t btnid) const;
private:
std::string binaryPath;
const std::string file = "soundboard.json";

View File

@ -39,6 +39,7 @@ private slots:
void saveConfig();
void openButtonManager();
void openSettings();
void openSearch();
signals:
void newStatusMessage(const QString&);
@ -48,6 +49,7 @@ private:
void loadShortcuts();
void removeAllButtons();
void loadButtonsFromConfig();
void playButtonID(uint32_t buttonid);
void reselectNext(std::function<void(uint8_t&,uint8_t&)> stepf);
QLayoutItem* findNextButton(std::function<void(uint8_t&,uint8_t&)> stepf);
@ -63,6 +65,7 @@ private:
QxtGlobalShortcut* left = nullptr;
QxtGlobalShortcut* right = nullptr;
QxtGlobalShortcut* play = nullptr;
QxtGlobalShortcut* search = nullptr;
// "real" coordinates
uint8_t xCoord = 0;

View File

@ -18,7 +18,7 @@ public:
float volume = 1.f;
};
explicit SoundButton(const std::string& name_ = "", const std::string& keycombo = "", QWidget* parent = nullptr);
explicit SoundButton(uint32_t id, const std::string& name_ = "", const std::string& keycombo = "", QWidget* parent = nullptr);
~SoundButton();
const std::string& getName() const;
@ -35,11 +35,10 @@ public slots:
private:
void setDisabled();
QString getInfo() const;
QString getInfo(bool withid = false) const;
static uint64_t nextid;
uint64_t id;
uint32_t id;
std::string name;
std::string keycombo;

View File

@ -19,6 +19,7 @@ DEFINES += QT_DEPRECATED_WARNINGS
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
SOURCES += \
src/buttonsearch.cpp \
src/main.cpp \
src/mainwindow.cpp \
src/sound.cpp \
@ -38,6 +39,7 @@ SOURCES += \
Log/Log.cpp
HEADERS += \
include/buttonsearch.h \
include/mainwindow.h \
include/sound.h \
include/sounddevice.h \
@ -63,6 +65,7 @@ OBJECTS_DIR = obj/
FORMS += \
ui/addnewwhat.ui \
ui/buttonmanager.ui \
ui/buttonsearch.ui \
ui/editbutton.ui \
ui/editsample.ui \
ui/mainwindow.ui \

163
src/buttonsearch.cpp Normal file
View File

@ -0,0 +1,163 @@
#include "buttonsearch.h"
#include "ui_buttonsearch.h"
#include <QVariant>
#include <Log.h>
static char getSymbol(uint8_t i) {
return (i < 10) ? ('0' + i) : ('a' + i-10);
}
static bool startsWith(const std::string& a, const std::string& b) {
// not very performant but working
return QString().fromStdString(a).toLower().startsWith(QString().fromStdString(b).toLower());
}
ButtonSearch::ButtonSearch(const Config& config, QWidget *parent) : QDialog(parent), config(config), ui(new Ui::ButtonSearch) {
ui->setupUi(this);
Qt::WindowFlags eFlags = windowFlags();
setWindowFlags(eFlags | Qt::WindowStaysOnTopHint | Qt::X11BypassWindowManagerHint);
esc = new QxtGlobalShortcut(QKeySequence("Esc"));
QObject::connect(esc, &QxtGlobalShortcut::activated, [this](){ buttonPressed(-1); });
enter = new QxtGlobalShortcut(QKeySequence("Enter"));
QObject::connect(enter, &QxtGlobalShortcut::activated, [this](){ buttonPressed(-2); });
up = new QxtGlobalShortcut(QKeySequence("Up"));
QObject::connect(up, &QxtGlobalShortcut::activated, std::bind(&ButtonSearch::selectionUp, this));
down = new QxtGlobalShortcut(QKeySequence("Down"));
QObject::connect(down, &QxtGlobalShortcut::activated, std::bind(&ButtonSearch::selectionDown, this));
for(uint8_t i = 0; i < NUMCOUNT-10; ++i) {
nums[i] = new QxtGlobalShortcut(QKeySequence(QString() + getSymbol(i)));
QObject::connect(nums[i], &QxtGlobalShortcut::activated, [this, i](){ buttonPressed(i); });
}
// numpad numbers
for(uint8_t i = 0; i < 10; ++i) {
nums[NUMCOUNT-10 +i] = new QxtGlobalShortcut(QKeySequence(QString("Num+") + getSymbol(i)));
QObject::connect(nums[NUMCOUNT-10 +i], &QxtGlobalShortcut::activated, [this, i](){ buttonPressed(i); });
}
}
void ButtonSearch::buttonPressed(int i) {
if(i == -1) {
// esc
Log::info << "esc pressed";
done(-1);
} else if(i == -2) {
// enter
Log::info << "enter pressed";
closeSearch();
} else {
char sym = getSymbol(i);
Log::info << "button pressed: " << i << " sym: " << getSymbol(i);
ui->searchText->setText(ui->searchText->toPlainText() + sym);
ui->searchResults->clear();
// execute search
const std::string searchStr = ui->searchText->toPlainText().toStdString();
for(const auto& row : config.rootConfig.buttons) {
for(const auto& btn : row) {
if(startsWith(btn.name, searchStr)) {
QListWidgetItem* item = new QListWidgetItem(QString::fromStdString(btn.name));
item->setData(Qt::UserRole, QVariant(btn.id));
ui->searchResults->addItem(item);
} else if(startsWith(std::to_string(btn.id), searchStr)) {
QListWidgetItem* item = new QListWidgetItem(QString::fromStdString(btn.name));
item->setData(Qt::UserRole, QVariant(btn.id));
ui->searchResults->addItem(item);
}
}
}
}
}
void ButtonSearch::closeSearch() {
QItemSelectionModel* selmod = ui->searchResults->selectionModel();
auto idx = selmod->selectedRows();
if(idx.isEmpty()) {
if(ui->searchResults->count() == 1) {
Log::info << "only one selected";
QVariant var = ui->searchResults->item(0)->data(Qt::UserRole);
assert(var.type() == QVariant::Type::UInt);
done((int) var.toUInt());
return;
}
done(-1); // nothing selected
} else {
QVariant var = idx.at(0).data(Qt::UserRole);
assert(var.type() == QVariant::Type::UInt);
done((int) var.toUInt());
}
}
void ButtonSearch::selectionDown() {
Log::info << "down";
if(ui->searchResults->count() == 0) {
Log::warn << "empty search results";
return; // avoid empty list
}
QItemSelectionModel* selmod = ui->searchResults->selectionModel();
QAbstractItemModel* itemmod = ui->searchResults->model();
auto idx = selmod->selectedRows();
if(idx.isEmpty()) {
// none selected - select first
Log::debug << "select first";
selmod->setCurrentIndex(itemmod->index(0, 0), QItemSelectionModel::SelectionFlag::ClearAndSelect);
} else {
// select next
int selectedRow = idx.at(0).row();
Log::debug << "idx: " << selectedRow;
if(ui->searchResults->count() > selectedRow+1) {
selmod->setCurrentIndex(itemmod->index(selectedRow+1, 0), QItemSelectionModel::SelectionFlag::ClearAndSelect);
} else {
// wrap around
selmod->setCurrentIndex(itemmod->index(0, 0), QItemSelectionModel::SelectionFlag::ClearAndSelect);
}
}
}
void ButtonSearch::selectionUp() {
Log::info << "up";
if(ui->searchResults->count() == 0) {
Log::warn << "empty search results";
return; // avoid empty list
}
QItemSelectionModel* selmod = ui->searchResults->selectionModel();
QAbstractItemModel* itemmod = ui->searchResults->model();
auto idx = selmod->selectedRows();
if(idx.isEmpty()) {
// none selected - select last
Log::debug << "select last";
selmod->setCurrentIndex(itemmod->index(itemmod->rowCount()-1, 0), QItemSelectionModel::SelectionFlag::ClearAndSelect);
} else {
// select next
int selectedRow = idx.at(0).row();
Log::debug << "idx: " << selectedRow;
if(selectedRow-1 >= 0) {
selmod->setCurrentIndex(itemmod->index(selectedRow-1, 0), QItemSelectionModel::SelectionFlag::ClearAndSelect);
} else {
// wrap around
selmod->setCurrentIndex(itemmod->index(itemmod->rowCount()-1, 0), QItemSelectionModel::SelectionFlag::ClearAndSelect);
}
}
}
ButtonSearch::~ButtonSearch() {
delete ui;
delete esc;
delete enter;
for(uint8_t i = 0; i < NUMCOUNT; ++i) {
delete nums[i];
}
}

View File

@ -56,6 +56,7 @@ void Config::load() {
try {
stream >> json;
rootConfig = json;
assignIDs();
} catch(nlohmann::detail::parse_error& pe) {
std::cout << "json error: " << pe.what() << std::endl;
return;
@ -72,7 +73,31 @@ void Config::save() {
if(stream) {
json j = rootConfig;
stream << j.dump(1, '\t');
assignIDs();
} else {
Log::error << "could not write configfile";
}
}
void Config::assignIDs() {
for(uint32_t row = 0; row < rootConfig.buttons.size(); ++row) {
auto& r = rootConfig.buttons.at(row);
for(uint32_t col = 0; col < r.size(); ++col) {
auto& btn = r.at(col);
btn.id = ((row+1) * 100) + (col+1); // TODO: problems when more than 99 colums in one row but should be fine for now
}
}
}
const Config::ButtonConfig* Config::getButtonByID(uint32_t btnid) const {
uint32_t row = (btnid / 100) -1;
uint32_t col = (btnid % 100) -1;
if(row >= rootConfig.buttons.size()) return nullptr;
const auto& rowref = rootConfig.buttons.at(row);
if(col >= rowref.size()) return nullptr;
return &rowref.at(col);
}

View File

@ -57,6 +57,7 @@ void from_json(const json& j, Config::ShortcutConfig& sc) {
sc.left = j.value("left", "");
sc.right = j.value("right", "");
sc.play = j.value("play", "");
sc.search = j.value("search", "");
sc.stop = j.value("stop", "");
}

View File

@ -4,6 +4,7 @@
#include "buttonmanager.h"
#include "editsample.h"
#include "settings.h"
#include "buttonsearch.h"
#include <QFileDialog>
#include <QMessageBox>
@ -36,6 +37,7 @@ MainWindow::~MainWindow() {
delete left;
delete right;
delete play;
delete search;
Sound::deinitInstance();
}
@ -147,6 +149,19 @@ void MainWindow::openSettings() {
}
}
void MainWindow::openSearch() {
Log::info << "openSearch";
ButtonSearch* bs = new ButtonSearch(config);
int res = bs->exec();
if(res >= 0) {
playButtonID(res);
}
delete bs;
}
void MainWindow::loadSoundFromConfig() {
Sound& sound = Sound::instance(); // init sound
sound.reset();
@ -176,6 +191,7 @@ void MainWindow::loadShortcuts() {
delete left;
delete right;
delete play;
delete search;
delete stopGlobal;
const Config::ShortcutConfig& sc = config.rootConfig.shortcuts;
@ -184,6 +200,7 @@ void MainWindow::loadShortcuts() {
left = loadShortcut(sc.left);
right = loadShortcut(sc.right);
play = loadShortcut(sc.play);
search = loadShortcut(sc.search);
stopGlobal = loadShortcut(sc.stop);
QObject::connect(up, SIGNAL( activated() ), this, SLOT( moveUp() ));
@ -191,6 +208,7 @@ void MainWindow::loadShortcuts() {
QObject::connect(left, SIGNAL( activated() ), this, SLOT( moveLeft() ));
QObject::connect(right, SIGNAL( activated() ), this, SLOT( moveRight() ));
QObject::connect(play, SIGNAL( activated() ), this, SLOT( playCurrent() ));
QObject::connect(search, SIGNAL( activated() ), this, SLOT( openSearch() ));
QObject::connect(stopGlobal, SIGNAL( activated() ), this, SLOT( stop() ));
}
@ -213,7 +231,7 @@ void MainWindow::loadButtonsFromConfig() {
for (const Config::ButtonConfig& bc : line) {
if (!bc.isValid()) continue;
SoundButton* sb = new SoundButton(bc.name, bc.key);
SoundButton* sb = new SoundButton(bc.id, bc.name, bc.key);
for(const Config::SampleConfig& sc : bc.samples) {
if(!sc.isValid()) continue;
@ -245,6 +263,21 @@ void MainWindow::loadButtonsFromConfig() {
// ui->gridLayout->setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred));
}
void MainWindow::playButtonID(uint32_t buttonid) {
const Config::ButtonConfig* btnconf = config.getButtonByID(buttonid);
if(!btnconf) {
Log::error << "invalid button id triggered: " << buttonid;
return;
}
SoundButton* sb = findChild<SoundButton*>(QString::fromStdString("soundButton" + std::to_string(buttonid)));
if(sb == nullptr) {
Log::error << "button not found: " << buttonid;
return;
}
sb->click();
}
void MainWindow::reselectNext(std::function<void(uint8_t&,uint8_t&)> stepf) {
QLayoutItem* item = ui->gridLayout->itemAtPosition(yCoord, xCoord);
QLayoutItem* newitem = findNextButton(stepf);

View File

@ -6,14 +6,12 @@
#include <Log.h>
uint64_t SoundButton::nextid = 0;
SoundButton::SoundButton(const std::string& name_, const std::string& keycombo, QWidget* parent) : QPushButton(parent), id(nextid++), name(name_), keycombo(keycombo) {
QString info = getInfo();
setText(info);
SoundButton::SoundButton(uint32_t id, const std::string& name_, const std::string& keycombo, QWidget* parent) : QPushButton(parent), id(id), name(name_), keycombo(keycombo) {
setText(getInfo(false));
setObjectName(QString::fromStdString("soundButton" + std::to_string(id)));
setMinimumSize(QSize(80, 20));
setToolTip(info);
setToolTip(getInfo(true));
setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred));
@ -50,6 +48,7 @@ void SoundButton::paintEvent(QPaintEvent* event) {
QPushButton::paintEvent(event);
QPainter painter(this);
// highlight
if(highlighted) {
QSize s = size();
QRect rect(HIGHLIGHTBORDEROFFSET, HIGHLIGHTBORDEROFFSET, s.width() - HIGHLIGHTBORDEROFFSET*2, s.height() - HIGHLIGHTBORDEROFFSET*2);
@ -57,14 +56,21 @@ void SoundButton::paintEvent(QPaintEvent* event) {
painter.drawRect(rect);
}
// sample count
if(samples.size() > 1) {
QString text = QString::fromStdString(std::to_string(currentSample+1) + "/" + std::to_string(samples.size()));
const QString text = QString::fromStdString(std::to_string(currentSample+1) + "/" + std::to_string(samples.size()));
painter.setPen(QPen(Qt::white));
QPoint textPos(0, painter.fontMetrics().height());
textPos.rx() = width() - painter.fontMetrics().horizontalAdvance(text) - 6;
painter.drawText(textPos, text);
}
// button id
const QString idtext = QString::fromStdString(std::to_string(id));
painter.setPen(QPen(Qt::gray));
QPoint textPos(6, painter.fontMetrics().height());
painter.drawText(textPos, idtext);
}
void SoundButton::play() {
@ -95,6 +101,6 @@ void SoundButton::setDisabled() {
setEnabled(false);
}
QString SoundButton::getInfo() const {
return QString::fromStdString(name + (keycombo.empty() ? "" : "\n" + keycombo));
QString SoundButton::getInfo(bool withid) const {
return QString::fromStdString(name + (keycombo.empty() ? "" : "\n" + keycombo) + (withid ? "\nID: " + std::to_string(id) : ""));
}

109
ui/buttonsearch.ui Normal file
View File

@ -0,0 +1,109 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ButtonSearch</class>
<widget class="QDialog" name="ButtonSearch">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>300</height>
</rect>
</property>
<property name="windowTitle">
<string>ButtonSearch</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QPushButton" name="closeButton">
<property name="text">
<string>Close</string>
</property>
</widget>
</item>
<item>
<widget class="QTextEdit" name="searchText">
<property name="maximumSize">
<size>
<width>16777215</width>
<height>40</height>
</size>
</property>
</widget>
</item>
<item>
<widget class="QListWidget" name="searchResults"/>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>closeButton</sender>
<signal>clicked()</signal>
<receiver>ButtonSearch</receiver>
<slot>closeSearch()</slot>
<hints>
<hint type="sourcelabel">
<x>169</x>
<y>29</y>
</hint>
<hint type="destinationlabel">
<x>398</x>
<y>48</y>
</hint>
</hints>
</connection>
<connection>
<sender>searchResults</sender>
<signal>itemDoubleClicked(QListWidgetItem*)</signal>
<receiver>ButtonSearch</receiver>
<slot>closeSearch()</slot>
<hints>
<hint type="sourcelabel">
<x>263</x>
<y>204</y>
</hint>
<hint type="destinationlabel">
<x>398</x>
<y>198</y>
</hint>
</hints>
</connection>
<connection>
<sender>searchResults</sender>
<signal>itemClicked(QListWidgetItem*)</signal>
<receiver>ButtonSearch</receiver>
<slot>closeSearch()</slot>
<hints>
<hint type="sourcelabel">
<x>339</x>
<y>227</y>
</hint>
<hint type="destinationlabel">
<x>397</x>
<y>247</y>
</hint>
</hints>
</connection>
<connection>
<sender>searchResults</sender>
<signal>itemActivated(QListWidgetItem*)</signal>
<receiver>ButtonSearch</receiver>
<slot>closeSearch()</slot>
<hints>
<hint type="sourcelabel">
<x>358</x>
<y>281</y>
</hint>
<hint type="destinationlabel">
<x>395</x>
<y>279</y>
</hint>
</hints>
</connection>
</connections>
<slots>
<slot>closeSearch()</slot>
</slots>
</ui>

View File

@ -125,6 +125,14 @@
<string>Ctrl+R</string>
</property>
</action>
<action name="actionTEST">
<property name="text">
<string>TEST</string>
</property>
<property name="shortcut">
<string>Ctrl+T</string>
</property>
</action>
</widget>
<resources/>
<connections/>