18 Commits

Author SHA1 Message Date
7bc42bf01e [sc9k] Fix signal/slot disconnect in SlideView::songListUpdate 2022-07-31 08:48:13 -05:00
9f29b58522 [sc9k] Fix to SlideView to use correct signal from QListWidget 2022-07-24 09:23:07 -05:00
564ed77c9a [sc9k] Swap song view and slide view, fix item lookup in song view 2022-07-24 08:56:17 -05:00
7ca3b810fc [sc9k] Make song selector a QListWidget instead of QComboBox 2022-07-24 00:35:11 -05:00
68d963ab69 [buildcore] Update buildcore 2022-07-24 00:34:52 -05:00
6f6f77f104 Get rid of "in Both" language 2022-01-30 21:45:03 -06:00
e57ba9e8f2 Eliminate now redundant tool tip for Show in Both button 2022-01-30 21:18:03 -06:00
382e09d4b4 Collapse slide controls into one line 2022-01-30 19:43:19 -06:00
181e1b8599 Combine Show in OpenLP and Hide in OBS buttons 2022-01-30 19:22:50 -06:00
3115083267 Fix show/hide button order and naming 2022-01-30 19:10:06 -06:00
2d5af03724 Update button layout and keyboard shortcut 2022-01-30 18:59:11 -06:00
c1cab3e3f3 [buildcore] Update buildcore 2022-01-30 18:56:46 -06:00
8d0b0fb4c5 Fix next song in status bar 2021-10-24 16:01:23 -05:00
f9122c2942 Fix typo n Next Song button 2021-10-24 16:01:23 -05:00
b0eeb81592 Add next song to status line 2021-10-24 16:01:23 -05:00
7999cc486f Fix OBS general GET reply handling 2021-10-24 16:01:23 -05:00
09065f3e92 Make OBSClient only delete reply upon full receipt 2021-09-19 12:46:33 -05:00
0ff1dfd300 Add obs scene switcher script 2021-09-19 12:43:52 -05:00
12 changed files with 255 additions and 99 deletions

View File

@ -11,7 +11,7 @@ set(CMAKE_INSTALL_PREFIX "${CMAKE_SOURCE_DIR}/dist/${BUILDCORE_BUILD_CONFIG}")
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
# enable ccache
@ -26,9 +26,14 @@ if(CMAKE_BUILD_TYPE STREQUAL "Debug")
add_definitions(-DDEBUG)
else()
add_definitions(-DNDEBUG)
if(APPLE)
set(CMAKE_OSX_ARCHITECTURES arm64;x86_64)
endif()
endif()
if(NOT MSVC)
if(MSVC)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /Zc:preprocessor")
else()
# forces colored output when using ninja
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fdiagnostics-color")
# enable warnings
@ -39,7 +44,7 @@ if(NOT MSVC)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wformat=2")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wmissing-field-initializers")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wnon-virtual-dtor")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wnull-dereference")
#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wnull-dereference")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wold-style-cast")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Woverloaded-virtual")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wpedantic")

View File

@ -16,16 +16,27 @@ else
HOST_ENV=${OS}-$(shell uname -m)
endif
ifeq ($(shell python -c 'import sys; print(sys.version_info[0])'),3)
PYTHON3=python
else
DEVENV=devenv$(shell pwd | sed 's/\//-/g')
DEVENV_IMAGE=${PROJECT_NAME}-devenv
ifneq ($(shell which docker 2> /dev/null),)
ifeq ($(shell docker inspect --format="{{.State.Status}}" ${DEVENV} 2>&1),running)
ENV_RUN=docker exec -i -t --user $(shell id -u ${USER}) ${DEVENV}
endif
endif
ifneq ($(shell which python3 2> /dev/null),)
PYTHON3=python3
else
ifeq ($(shell ${ENV_RUN} python -c 'import sys; print(sys.version_info[0])'),3)
PYTHON3=python
endif
endif
SCRIPTS=${BUILDCORE_PATH}/scripts
SETUP_BUILD=${PYTHON3} ${SCRIPTS}/setup-build.py
PYBB=${PYTHON3} ${SCRIPTS}/pybb.py
CMAKE_BUILD=${PYBB} cmake-build
CTEST=${PYBB} ctest-all
RM_RF=${PYBB} rm
ifdef USE_VCPKG
ifndef VCPKG_DIR_BASE
@ -37,19 +48,12 @@ ifdef USE_VCPKG
VCPKG_TOOLCHAIN=--toolchain=${VCPKG_DIR}/scripts/buildsystems/vcpkg.cmake
endif
ifeq ($(OS),darwin)
DEBUGGER=lldb
DEBUGGER=lldb --
else
DEBUGGER=gdb --args
endif
VCPKG_DIR=$(VCPKG_DIR_BASE)/$(VCPKG_VERSION)-$(HOST_ENV)
DEVENV=devenv$(shell pwd | sed 's/\//-/g')
DEVENV_IMAGE=${PROJECT_NAME}-devenv
ifneq ($(shell which docker 2> /dev/null),)
ifeq ($(shell docker inspect --format="{{.State.Status}}" ${DEVENV} 2>&1),running)
ENV_RUN=docker exec -i -t --user $(shell id -u ${USER}) ${DEVENV}
endif
endif
CURRENT_BUILD=$(HOST_ENV)-$(shell ${PYBB} cat .current_build)
.PHONY: build
@ -69,6 +73,12 @@ purge:
.PHONY: test
test: build
${ENV_RUN} ${CMAKE_BUILD} build test
.PHONY: test-verbose
test-verbose: build
${ENV_RUN} ${CTEST} build --output-on-failure
.PHONY: test-rerun-verbose
test-rerun-verbose: build
${ENV_RUN} ${CTEST} build --rerun-failed --output-on-failure
.PHONY: devenv-image
devenv-image:
@ -89,11 +99,14 @@ devenv-create:
.PHONY: devenv-destroy
devenv-destroy:
docker rm -f ${DEVENV}
ifdef ENV_RUN
.PHONY: devenv-shell
devenv-shell:
${ENV_RUN} bash
endif
ifdef USE_VCPKG
.PHONY: vcpkg
vcpkg: ${VCPKG_DIR} vcpkg-install
@ -114,18 +127,24 @@ ifneq (${OS},windows)
else
${VCPKG_DIR}/vcpkg install --triplet x64-windows ${VCPKG_PKGS}
endif
else # USE_VCPKG
else ifdef USE_CONAN # USE_VCPKG ################################################
.PHONY: setup-conan
conan-config:
conan profile new nostalgia --detect --force
${ENV_RUN} conan profile new ${PROJECT_NAME} --detect --force
ifeq ($(OS),linux)
conan profile update settings.compiler.libcxx=libstdc++11 ${PROJECT_NAME}
${ENV_RUN} conan profile update settings.compiler.libcxx=libstdc++11 ${PROJECT_NAME}
else
${ENV_RUN} conan profile update settings.compiler.cppstd=20 ${PROJECT_NAME}
ifeq ($(OS),windows)
${ENV_RUN} conan profile update settings.compiler.runtime=static ${PROJECT_NAME}
endif
endif
.PHONY: conan
conan:
@mkdir -p .conanbuild && cd .conanbuild && conan install ../ --build=missing -pr=${PROJECT_NAME}
endif # USE_VCPKG
${ENV_RUN} ${PYBB} conan-install ${PROJECT_NAME}
endif # USE_VCPKG ###############################################
.PHONY: configure-xcode
configure-xcode:

View File

@ -8,7 +8,7 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
#
# "Python Busy Box" - adds cross platform equivalents to Unix commands that
# "Python Busy Box" - adds cross-platform equivalents to Unix commands that
# don't translate well to that other operating system
import os
@ -17,31 +17,56 @@ import subprocess
import sys
def cat(path):
try:
with open(path) as f:
data = f.read()
print(data)
def cat(paths: [str]) -> int:
for path in paths:
try:
with open(path) as f:
data = f.read()
sys.stdout.write(data)
except FileNotFoundError:
sys.stderr.write('cat: {}: no such file or directory\n'.format(path))
return 1
sys.stdout.write('\n')
return 0
def mkdir(path: str) -> int:
if not os.path.exists(path):
try:
os.mkdir(path)
except:
return 1
return 0
except FileNotFoundError:
sys.stderr.write('cat: {}: no such file or directory\n'.format(path))
return 1
def mkdir(path):
if not os.path.exists(path) and os.path.isdir(path):
os.mkdir(path)
if os.path.isdir(path):
return 0
return 1
# this exists because Windows is utterly incapable of providing a proper rm -rf
def rm(path):
def rm(path: str) -> int:
if (os.path.exists(path) or os.path.islink(path)) and not os.path.isdir(path):
os.remove(path)
elif os.path.isdir(path):
shutil.rmtree(path)
return 0
def cmake_build(base_path, target):
def ctest_all() -> int:
base_path = sys.argv[2]
if not os.path.isdir(base_path):
# no generated projects
return 0
args = ['ctest'] + sys.argv[3:]
orig_dir = os.getcwd()
for d in os.listdir(base_path):
os.chdir(os.path.join(orig_dir, base_path, d))
err = subprocess.run(args).returncode
if err != 0:
return err
return 0
def cmake_build(base_path: str, target: str) -> int:
if not os.path.isdir(base_path):
# nothing to build
return 0
@ -52,24 +77,47 @@ def cmake_build(base_path, target):
err = subprocess.run(args).returncode
if err != 0:
return err
return 0
def conan() -> int:
project_name = sys.argv[2]
conan_dir = '.conanbuild'
err = mkdir(conan_dir)
if err != 0:
return err
args = ['conan', 'install', '../', '--build=missing', '-pr', project_name]
os.chdir(conan_dir)
err = subprocess.run(args).returncode
if err != 0:
return err
return 0
def main():
err = 0
if sys.argv[1] == 'mkdir':
mkdir(sys.argv[2])
err = mkdir(sys.argv[2])
elif sys.argv[1] == 'rm':
for i in range(2, len(sys.argv)):
rm(sys.argv[i])
elif sys.argv[1] == 'conan-install':
err = conan()
elif sys.argv[1] == 'ctest-all':
err = ctest_all()
elif sys.argv[1] == 'cmake-build':
err = cmake_build(sys.argv[2], sys.argv[3] if len(sys.argv) > 3 else None)
sys.exit(err)
elif sys.argv[1] == 'cat':
err = cat(sys.argv[2])
sys.exit(err)
err = cat(sys.argv[2:])
else:
sys.stderr.write('Command not found\n')
err = 1
return err
if __name__ == '__main__':
try:
main()
err = main()
sys.exit(err)
except KeyboardInterrupt:
sys.exit(1)

View File

@ -66,15 +66,21 @@ def main():
build_dir = '{:s}/build/{:s}'.format(project_dir, build_config)
rm(build_dir)
mkdir(build_dir)
subprocess.run(['cmake', '-S', project_dir, '-B', build_dir, build_tool,
'-DCMAKE_EXPORT_COMPILE_COMMANDS=ON',
'-DCMAKE_TOOLCHAIN_FILE={:s}'.format(args.toolchain),
'-DCMAKE_BUILD_TYPE={:s}'.format(build_type_arg),
'-DUSE_ASAN={:s}'.format(sanitizer_status),
'-DBUILDCORE_BUILD_CONFIG={:s}'.format(build_config),
'-DBUILDCORE_TARGET={:s}'.format(args.target),
qt_path,
])
cmake_cmd = [
'cmake', '-S', project_dir, '-B', build_dir, build_tool,
'-DCMAKE_EXPORT_COMPILE_COMMANDS=ON',
'-DCMAKE_TOOLCHAIN_FILE={:s}'.format(args.toolchain),
'-DCMAKE_BUILD_TYPE={:s}'.format(build_type_arg),
'-DUSE_ASAN={:s}'.format(sanitizer_status),
'-DBUILDCORE_BUILD_CONFIG={:s}'.format(build_config),
'-DBUILDCORE_TARGET={:s}'.format(args.target),
]
if qt_path != '':
cmake_cmd.append(qt_path)
if platform.system() == 'Windows':
cmake_cmd.append('-A x64')
subprocess.run(cmake_cmd)
mkdir('dist')
if int(args.current_build) != 0:

41
obs_scene_switcher.py Normal file
View File

@ -0,0 +1,41 @@
from http.server import HTTPServer, BaseHTTPRequestHandler
from urllib.parse import urlparse, parse_qs
import threading
import obspython as obs
def set_current_scene(scene_name):
scenes = obs.obs_frontend_get_scenes()
for scene in scenes:
name = obs.obs_source_get_name(scene)
if name == scene_name:
obs.obs_frontend_set_current_scene(scene)
return 0
return 1
class RqstHandler(BaseHTTPRequestHandler):
def do_GET(self):
up = urlparse(self.path)
if up.path == '/Scene':
qc = parse_qs(up.query)
set_current_scene(qc.get('name', [''])[0])
self.send_response(200)
self.end_headers()
elif up.path == '/ping':
self.send_response(200)
self.end_headers()
def log_message(self, format, *args):
pass
def run(name):
httpd = HTTPServer(('127.0.0.1', 9302), RqstHandler)
httpd.serve_forever()
t = threading.Thread(target=run, args=(1,), daemon=True)
t.start()

View File

@ -6,9 +6,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#include <QFormLayout>
#include <QHBoxLayout>
#include <QLineEdit>
#include <QPushButton>
#include <QStatusBar>
@ -22,57 +20,56 @@ MainWindow::MainWindow(QWidget *parent): QMainWindow(parent) {
const auto mainWidget = new QWidget(this);
const auto rootLyt = new QVBoxLayout;
const auto controlsLayout = new QGridLayout;
const auto slideView = new SlideView(this);
m_slideView = new SlideView(this);
setCentralWidget(mainWidget);
mainWidget->setLayout(rootLyt);
rootLyt->addWidget(slideView);
rootLyt->addWidget(m_slideView);
rootLyt->addLayout(controlsLayout);
// setup slide controls
const auto showHideLyt = new QHBoxLayout;
rootLyt->addLayout(showHideLyt);
const auto btnPrevSong = new QPushButton(tr("Previous Song (Left)"), this);
const auto btnPrevSlide = new QPushButton(tr("Previous Slide (Up)"), this);
const auto btnNextSlide = new QPushButton(tr("Next Slide (Down)"), this);
const auto btnNextSong = new QPushButton(tr("Next Song (Right))"), this);
const auto btnBlankSlides = new QPushButton(tr("Blank Slides (,)"), this);
const auto btnShowSlides = new QPushButton(tr("Show Slides (.)"), this);
controlsLayout->addWidget(btnPrevSlide, 0, 0);
controlsLayout->addWidget(btnNextSlide, 0, 1);
controlsLayout->addWidget(btnPrevSong, 1, 0);
controlsLayout->addWidget(btnNextSong, 1, 1);
controlsLayout->addWidget(btnBlankSlides, 2, 0);
controlsLayout->addWidget(btnShowSlides, 2, 1);
const auto btnNextSong = new QPushButton(tr("Next Song (Right)"), this);
const auto btnHideSlides = new QPushButton(tr("Hide (1)"), this);
const auto btnOpenLpShowSlides = new QPushButton(tr("Show in OpenLP Only (2)"), this);
const auto btnShowSlides = new QPushButton(tr("Show (3)"), mainWidget);
controlsLayout->addWidget(btnPrevSlide, 0, 1);
controlsLayout->addWidget(btnNextSlide, 0, 2);
controlsLayout->addWidget(btnPrevSong, 0, 0);
controlsLayout->addWidget(btnNextSong, 0, 3);
showHideLyt->addWidget(btnHideSlides);
showHideLyt->addWidget(btnOpenLpShowSlides);
showHideLyt->addWidget(btnShowSlides);
btnNextSong->setShortcut(Qt::Key_Right);
btnPrevSong->setShortcut(Qt::Key_Left);
btnNextSlide->setShortcut(Qt::Key_Down);
btnPrevSlide->setShortcut(Qt::Key_Up);
btnBlankSlides->setShortcut(Qt::Key_Comma);
btnShowSlides->setShortcut(Qt::Key_Period);
btnBlankSlides->setToolTip(tr("Also hides slides in OBS"));
btnHideSlides->setShortcut(Qt::Key_1);
btnOpenLpShowSlides->setShortcut(Qt::Key_2);
btnHideSlides->setToolTip(tr("Also hides slides in OBS"));
btnShowSlides->setShortcut(Qt::Key_3);
connect(btnNextSlide, &QPushButton::clicked, &m_openlpClient, &OpenLPClient::nextSlide);
connect(btnPrevSlide, &QPushButton::clicked, &m_openlpClient, &OpenLPClient::prevSlide);
connect(btnNextSong, &QPushButton::clicked, &m_openlpClient, &OpenLPClient::nextSong);
connect(btnPrevSong, &QPushButton::clicked, &m_openlpClient, &OpenLPClient::prevSong);
connect(btnBlankSlides, &QPushButton::clicked, &m_openlpClient, &OpenLPClient::blankScreen);
connect(btnBlankSlides, &QPushButton::clicked, &m_obsClient, &OBSClient::hideSlides);
connect(btnHideSlides, &QPushButton::clicked, &m_openlpClient, &OpenLPClient::blankScreen);
connect(btnHideSlides, &QPushButton::clicked, &m_obsClient, &OBSClient::hideSlides);
connect(btnOpenLpShowSlides, &QPushButton::clicked, &m_openlpClient, &OpenLPClient::showSlides);
connect(btnShowSlides, &QPushButton::clicked, &m_obsClient, &OBSClient::showSlides);
connect(btnShowSlides, &QPushButton::clicked, &m_openlpClient, &OpenLPClient::showSlides);
connect(&m_openlpClient, &OpenLPClient::pollUpdate, slideView, &SlideView::pollUpdate);
connect(&m_openlpClient, &OpenLPClient::songListUpdate, slideView, &SlideView::songListUpdate);
connect(&m_openlpClient, &OpenLPClient::slideListUpdate, slideView, &SlideView::slideListUpdate);
connect(&m_openlpClient, &OpenLPClient::pollFailed, slideView, &SlideView::reset);
connect(slideView, &SlideView::songChanged, &m_openlpClient, &OpenLPClient::changeSong);
connect(slideView, &SlideView::slideChanged, &m_openlpClient, &OpenLPClient::changeSlide);
connect(&m_openlpClient, &OpenLPClient::pollUpdate, m_slideView, &SlideView::pollUpdate);
connect(&m_openlpClient, &OpenLPClient::songListUpdate, m_slideView, &SlideView::songListUpdate);
connect(&m_openlpClient, &OpenLPClient::slideListUpdate, m_slideView, &SlideView::slideListUpdate);
connect(&m_openlpClient, &OpenLPClient::pollFailed, m_slideView, &SlideView::reset);
connect(m_slideView, &SlideView::songChanged, &m_openlpClient, &OpenLPClient::changeSong);
connect(m_slideView, &SlideView::slideChanged, &m_openlpClient, &OpenLPClient::changeSlide);
// setup scene selector
const auto btnObsHideSlides = new QPushButton(tr("Hide Slides in OBS (;)"), mainWidget);
const auto btnObsShowSlides = new QPushButton(tr("Show Slides in OBS (')"), mainWidget);
controlsLayout->addWidget(btnObsHideSlides, 3, 0);
controlsLayout->addWidget(btnObsShowSlides, 3, 1);
btnObsHideSlides->setShortcut(Qt::Key_Semicolon);
btnObsShowSlides->setShortcut(Qt::Key_Apostrophe);
btnObsShowSlides->setToolTip(tr("Also shows slides in OpenLP"));
connect(btnObsHideSlides, &QPushButton::clicked, &m_obsClient, &OBSClient::hideSlides);
connect(btnObsShowSlides, &QPushButton::clicked, &m_obsClient, &OBSClient::showSlides);
connect(btnObsShowSlides, &QPushButton::clicked, &m_openlpClient, &OpenLPClient::showSlides);
connect(btnOpenLpShowSlides, &QPushButton::clicked, &m_obsClient, &OBSClient::hideSlides);
// setup status bar
setStatusBar(new QStatusBar(this));
connect(&m_openlpClient, &OpenLPClient::songChanged, this, &MainWindow::refreshStatusBar);
connect(&m_openlpClient, &OpenLPClient::pollUpdate, this, &MainWindow::openLpConnectionInit);
connect(&m_obsClient, &OBSClient::pollUpdate, this, &MainWindow::obsConnectionInit);
refreshStatusBar();
@ -109,5 +106,7 @@ void MainWindow::obsConnectionLost() {
void MainWindow::refreshStatusBar() {
const auto openLpStatus = m_openLpConnected ? tr("OpenLP: Connected") : tr("OpenLP: Not Connected");
const auto obsStatus = m_obsConnected ? tr("OBS: Connected") : tr("OBS: Not Connected");
statusBar()->showMessage(openLpStatus + " | " + obsStatus);
const auto nextSong = m_openlpClient.getNextSong();
const auto nextSongTxt = m_openLpConnected ? " | Next Song: " + nextSong : "";
statusBar()->showMessage(openLpStatus + " | " + obsStatus + nextSongTxt);
}

View File

@ -21,6 +21,7 @@ class MainWindow: public QMainWindow {
private:
OBSClient m_obsClient;
OpenLPClient m_openlpClient;
class SlideView *m_slideView = nullptr;
bool m_openLpConnected = false;
bool m_obsConnected = false;

View File

@ -5,6 +5,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#include <QNetworkReply>
#include <QNetworkRequest>
#include <QUrl>
@ -40,7 +41,8 @@ void OBSClient::setSlidesVisible(int state) {
void OBSClient::get(QString urlExt) {
QUrl url(QString(BaseUrl) + urlExt);
QNetworkRequest rqst(url);
m_nam->get(rqst)->deleteLater();
auto reply = m_nam->get(rqst);
connect(reply, &QIODevice::readyRead, reply, &QObject::deleteLater);
}
void OBSClient::poll() {

View File

@ -24,6 +24,15 @@ OpenLPClient::OpenLPClient(QObject *parent): QObject(parent) {
connect(m_pollingNam, &QNetworkAccessManager::finished, this, &OpenLPClient::handlePollResponse);
}
QString OpenLPClient::getNextSong() {
const auto currentSong = m_songNameMap[m_currentSongId];
const auto songIdx = m_songList.indexOf(currentSong) + 1;
if (songIdx < m_songList.size()) {
return m_songList[songIdx];
}
return "";
}
void OpenLPClient::nextSlide() {
get("/api/controller/live/next");
}
@ -117,6 +126,7 @@ void OpenLPClient::handlePollResponse(QNetworkReply *reply) {
if (m_currentSongId != songId) {
requestSlideList();
m_currentSongId = songId;
emit songChanged(songId);
}
emit pollUpdate(m_songNameMap[songId], slide);
}
@ -130,18 +140,18 @@ void OpenLPClient::handleSongListResponse(QNetworkReply *reply) {
if (data.isEmpty()) {
return;
}
QStringList songList;
auto doc = QJsonDocument::fromJson(data);
auto items = doc.object()["results"].toObject()["items"].toArray();
m_songNameMap.clear();
m_songList.clear();
for (const auto &item : items) {
auto song = item.toObject();
auto name = song["title"].toString();
auto id = song["id"].toString();
m_songNameMap[id] = name;
songList.push_back(name);
m_songList.push_back(name);
}
emit songListUpdate(songList);
emit songListUpdate(m_songList);
}
void OpenLPClient::handleSlideListResponse(QNetworkReply *reply) {

View File

@ -30,13 +30,17 @@ class OpenLPClient: public QObject {
QNetworkAccessManager *m_slideListNam = new QNetworkAccessManager(this);
QTimer m_pollTimer;
QHash<QString, QString> m_songNameMap;
QStringList m_songList;
int m_currentServiceId = -1;
QString m_currentSongId;
public:
explicit OpenLPClient(QObject *parent = nullptr);
public slots:
[[nodiscard]]
QString getNextSong();
public slots:
void nextSlide();
void prevSlide();
@ -80,5 +84,6 @@ class OpenLPClient: public QObject {
void slideListUpdate(QStringList, QStringList);
void songChanged(QString);
};

View File

@ -8,15 +8,16 @@
#include <QComboBox>
#include <QDebug>
#include <QHBoxLayout>
#include <QHeaderView>
#include <QListWidget>
#include <QTableWidget>
#include <QVBoxLayout>
#include "slideview.hpp"
SlideView::SlideView(QWidget *parent): QWidget(parent) {
auto lyt = new QVBoxLayout(this);
m_songSelector = new QComboBox(this);
auto lyt = new QHBoxLayout(this);
m_songSelector = new QListWidget(this);
m_slideTable = new QTableWidget(this);
auto header = m_slideTable->horizontalHeader();
header->setVisible(false);
@ -28,15 +29,29 @@ SlideView::SlideView(QWidget *parent): QWidget(parent) {
#ifndef _WIN32
m_slideTable->setAlternatingRowColors(true);
#endif
lyt->addWidget(m_songSelector);
lyt->addWidget(m_slideTable);
lyt->addWidget(m_songSelector);
connect(m_slideTable, &QTableWidget::currentCellChanged, this, &SlideView::slideChanged);
}
QString SlideView::getNextSong() const {
const auto cnt = m_songSelector->count();
const auto idx = m_songSelector->currentRow() + 1;
if (idx < cnt) {
return m_songSelector->currentItem()->text();
}
return "";
}
void SlideView::pollUpdate(QString songName, int slide) {
if (songName != m_currentSong) {
auto songItems = m_songSelector->findItems(songName, Qt::MatchFixedString);
if (songItems.size() < 1) {
return;
}
auto songItem = songItems.first();
if (songItem != m_songSelector->currentItem()) {
m_currentSong = songName;
m_songSelector->setCurrentText(songName);
m_songSelector->setCurrentItem(songItem);
}
if (slide != m_currentSlide) {
m_currentSlide = slide;
@ -45,7 +60,8 @@ void SlideView::pollUpdate(QString songName, int slide) {
}
void SlideView::changeSong(int song) {
if (m_songSelector->currentText() != m_currentSong) {
auto songItem = m_songSelector->item(song);
if (songItem->text() != m_currentSong) {
emit songChanged(song);
}
}
@ -75,11 +91,11 @@ void SlideView::songListUpdate(QStringList songList) {
// We want to reset the song to 0 upon replacement,
// but leave it alone upon initialization.
auto isReplacement = m_songSelector->count() > 0;
disconnect(m_songSelector, SIGNAL(currentIndexChanged(int)), this, SLOT(changeSong(int)));
disconnect(m_songSelector, &QListWidget::currentRowChanged, this, &SlideView::changeSong);
m_songSelector->clear();
m_songSelector->addItems(songList);
if (isReplacement) {
changeSong(0);
}
connect(m_songSelector, SIGNAL(currentIndexChanged(int)), this, SLOT(changeSong(int)));
connect(m_songSelector, &QListWidget::currentRowChanged, this, &SlideView::changeSong);
}

View File

@ -13,12 +13,16 @@ class SlideView: public QWidget {
Q_OBJECT
private:
class QTableWidget *m_slideTable = nullptr;
class QComboBox *m_songSelector = nullptr;
class QListWidget *m_songSelector = nullptr;
QString m_currentSong;
int m_currentSlide = -1;
public:
explicit SlideView(QWidget *parent = nullptr);
[[nodiscard]]
QString getNextSong() const;
public slots:
void pollUpdate(QString songId, int slideNum);