diff --git a/assets.qrc b/assets.qrc
index 4ef5956..2206a8a 100644
--- a/assets.qrc
+++ b/assets.qrc
@@ -4,5 +4,7 @@
assets/notificationAreaIcon.png
assets/style.qss
assets/logo.ico
+ assets/mute.svg
+ assets/unmute.svg
diff --git a/bueno.bat b/bueno.bat
index 37b5dec..540be60 100644
--- a/bueno.bat
+++ b/bueno.bat
@@ -1,7 +1,7 @@
taskkill /F /IM "MixerQ.exe"
taskkill /F /IM "MixerQd.exe"
qmake -o build\Makefile .\qtest.pro
-mingw32-make.exe -C .\build -f Makefile.Release
+REM mingw32-make.exe -C .\build -f Makefile.Release
mingw32-make.exe -C .\build -f Makefile.Debug
-makensis /DBUILDTYPE=release install\installer.nsi
-makensis /DBUILDTYPE=debug install\installer.nsi
+REM makensis /DBUILDTYPE=release install\installer.nsi
+REM makensis /DBUILDTYPE=debug install\installer.nsi
diff --git a/qtest.pro b/qtest.pro
index 0568bca..42a1165 100644
--- a/qtest.pro
+++ b/qtest.pro
@@ -18,7 +18,7 @@ LIBS += -lWinmm -lodbc32 -lodbccp32 -luuid -loleaut32 -lole32 -lshell32 -ladvapi
DEFINES += QT_LOGGING_TO_CONSOLE=1 WIN32_LEAN_AND_MEAN _WIN32_WINNT=0x0602
DEFINES_DEBUG += DEBUG
-QT += widgets network
+QT += widgets network svg
INCLUDEPATH += "$$PWD\src" "$$PWD\src\qt" "$$PWD\src\back" "$$PWD\src\back\reimpl" "$$PWD\src\cont"
VPATH += "$$PWD\src" "$$PWD\src\qt" "$$PWD\src\back" "$$PWD\src\back\reimpl" "$$PWD\src\cont"
diff --git a/src/back/backlasses.cpp b/src/back/backlasses.cpp
index cead61f..74af6a0 100644
--- a/src/back/backlasses.cpp
+++ b/src/back/backlasses.cpp
@@ -234,6 +234,7 @@ HRESULT EndpointSituationCallback::OnDeviceRemoved(LPCWSTR pwstrDeviceId) {
HRESULT EndpointSituationCallback::OnDeviceStateChanged(LPCWSTR pwstrDeviceId, DWORD dwNewState) {
std::wstring endpointId = std::wstring(pwstrDeviceId);
+ log_wdebugcpp(L"Endpoint state change for " + endpointId);
EndpointState newState;
switch (dwNewState) {
case DEVICE_STATE_ACTIVE:
@@ -249,7 +250,7 @@ HRESULT EndpointSituationCallback::OnDeviceStateChanged(LPCWSTR pwstrDeviceId, D
newState = EndpointState::ENDPOINT_UNPLUGGED;
break;
}
- isEpStateChanging = true;
+ isEpStateChanging.exchange(true);
std::thread newEndpointThread(&OverseerHandler::reviseEndpointShowing, osh,
endpointId, newState);
newEndpointThread.detach();
@@ -258,7 +259,7 @@ HRESULT EndpointSituationCallback::OnDeviceStateChanged(LPCWSTR pwstrDeviceId, D
}
HRESULT EndpointSituationCallback::OnPropertyValueChanged(LPCWSTR pwstrDeviceId, const PROPERTYKEY key) {
- isEpStateChanging = true;
+ isEpStateChanging.exchange(true);
std::thread propertyThread(&Overseer::updateEndpointInfo, os, std::wstring(pwstrDeviceId));
propertyThread.detach();
while(isEpStateChanging);
@@ -266,7 +267,8 @@ HRESULT EndpointSituationCallback::OnPropertyValueChanged(LPCWSTR pwstrDeviceId,
}
void EndpointSituationCallback::reportFinishedStateChange() {
- this->isEpStateChanging = false;
+ this->isEpStateChanging.exchange(false);
+ return;
}
Endpoint::Endpoint(IMMDevice* ep, IPolicyConfig7* policyConfig, uint64_t idx) {
@@ -325,23 +327,29 @@ void Endpoint::activateEndpointSessions() {
return;
}
- if (FAILED(endpoint->Activate(__uuidof(IAudioSessionManager2),
- CLSCTX_ALL, NULL, (void**) &sessionManager))) {
- log_debugcpp("Couldn't open session manager2, huh");
- return;
+ if (!sessionManager) {
+ if (FAILED(endpoint->Activate(__uuidof(IAudioSessionManager2),
+ CLSCTX_ALL, NULL, (void**) &sessionManager))) {
+ log_debugcpp("Couldn't open session manager2, huh");
+ return;
+ }
}
+
IAudioSessionEnumerator* sessionEnumerator = nullptr;
- if (FAILED(sessionManager->GetSessionEnumerator(&sessionEnumerator))) { log_wdebugcpp(L"sesEnumeratorBros..."); return; }
+ if (FAILED(sessionManager->GetSessionEnumerator(&sessionEnumerator))) { log_wdebugcpp(L"sesEnumeratorBros..."); exit(-5); return; }
endpointSessions.resize(1, nullptr);
int sessionCount;
sessionEnumerator->GetCount(&sessionCount);
for (int i = 0; i < sessionCount; i++) {
IAudioSessionControl* sessionControlTmp;
- sessionEnumerator->GetSession(i, (IAudioSessionControl**)&sessionControlTmp);
+ if (FAILED(sessionEnumerator->GetSession(i, (IAudioSessionControl**)&sessionControlTmp))) {
+ exit(-6);
+ }
IAudioSessionControl2* sessionControl;
- sessionControlTmp->QueryInterface(__uuidof(IAudioSessionControl2), (void**)&sessionControl);
- sessionControl->AddRef();
+ if(FAILED(sessionControlTmp->QueryInterface(__uuidof(IAudioSessionControl2), (void**)&sessionControl))){
+ exit(-7);
+ }
sessionControlTmp->Release();
Session* session = new Session(this, sessionControl, (size_t)i);
if (sessionControl->IsSystemSoundsSession() == S_OK) endpointSessions[0] = session;
@@ -350,6 +358,13 @@ void Endpoint::activateEndpointSessions() {
sessionEnumerator->Release();
}
+/*
+ * void Endpoint::deleteSessionManager() {
+ * sessionManager->Release();
+ * sessionManager = nullptr;
+ * }
+ */
+
void Endpoint::addSession(Session* session) {
session->setIndex(this->getSessionCount());
endpointSessions.push_back(session);
@@ -357,9 +372,15 @@ void Endpoint::addSession(Session* session) {
void Endpoint::activateEndpointVolume() {
//If this EP is created after init, COM won't be initialized on the executing thread.
- HRESULT result = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
+ HRESULT result = CoInitializeEx(NULL, COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE);
if (this->endpointVolume == nullptr) {
- endpoint->Activate(IID_IAudioEndpointVolume, CLSCTX_ALL, NULL, (void**)&this->endpointVolume);
+ if(FAILED(endpoint->Activate(
+ IID_IAudioEndpointVolume,
+ CLSCTX_ALL,
+ NULL,
+ (void**)&this->endpointVolume))) {
+ log_debugcpp("No volume, huh");
+ }
//todo: check header
if(FAILED(endpoint->Activate(__uuidof(IAudioMeterInformation),
@@ -594,8 +615,8 @@ void Overseer::createEndpoints(Flows flow) {
/*
* Counting them
*/
- if(FAILED(deviceCollection->GetCount(&numEndpoints))) { log_debugcpp("si");};
- if(numEndpoints == 0) { log_debugcpp("si"); };
+ if(FAILED(deviceCollection->GetCount(&numEndpoints))) { log_debugcpp("si");};
+ if(numEndpoints == 0) { log_debugcpp("si"); };
/*
* Retrieving actual endpoints and storing them on their own collection
diff --git a/src/back/backlasses.h b/src/back/backlasses.h
index 9f79924..5acedba 100644
--- a/src/back/backlasses.h
+++ b/src/back/backlasses.h
@@ -82,6 +82,7 @@ class Endpoint {
void unregisterNewSessionNotification(EndpointNewSessionCallback* ensc);
void deleteSessions();
void activateEndpointSessions();
+ //void deleteSessionManager();
std::mutex endpointSessionsMutex;
~Endpoint();
@@ -91,12 +92,13 @@ class Endpoint {
std::vector endpointSessions;
uint32_t channelCount = 0;
IMMDevice *endpoint;
- IAudioClient *audioClient;
+ IAudioEndpointVolume *endpointVolume = nullptr;
+ IPropertyStore *properties;
+ IAudioMeterInformation *endpointPeakMeter = nullptr;
+ //IAudioClient *audioClient;
int64_t defTime, minTime;
IAudioSessionManager2 *sessionManager = nullptr;
Flows flow;
- IAudioEndpointVolume *endpointVolume = nullptr;
- IPropertyStore *properties;
std::wstring friendlyName;
std::wstring descriptionName;
std::wstring deviceName;
@@ -105,7 +107,6 @@ class Endpoint {
Roles endpointRoles = (Roles)0;
uint64_t idx;
//Not implemented in llvm-mingw. Sad! todo: mingw patch
- IAudioMeterInformation *endpointPeakMeter = nullptr;
IPolicyConfig7* policyConfig;
};
@@ -125,7 +126,7 @@ class EndpointVolumeCallback : public IAudioEndpointVolumeCallback {
private:
ULONG ref = 1;
Endpoint* ep;
- bool wait = false;
+ std::atomic wait = false;
};
class EndpointSituationCallback : public IMMNotificationClient {
@@ -143,7 +144,7 @@ class EndpointSituationCallback : public IMMNotificationClient {
private:
ULONG ref = 1;
Overseer* os;
- bool isEpStateChanging = false;
+ std::atomic isEpStateChanging = false;
};
class Overseer {
@@ -204,7 +205,7 @@ class EndpointNewSessionCallback : public IAudioSessionNotification {
void createSessionThread(SessionThreadParams params);
private:
- bool wait = false;
+ std::atomic wait = false;
ULONG ref = 1;
EndpointHandler *eph;
diff --git a/src/back/backsessionclasses.cpp b/src/back/backsessionclasses.cpp
index 5816ac5..c9aaa85 100644
--- a/src/back/backsessionclasses.cpp
+++ b/src/back/backsessionclasses.cpp
@@ -39,6 +39,7 @@ HRESULT SessionStateCallback::QueryInterface(REFIID riid, VOID **ppvInterface) {
HRESULT SessionStateCallback::OnDisplayNameChanged(LPCWSTR NewDisplayName, LPCGUID EventContext) {
//TODO: Preguntar
while(sh->getVolumeInfo()->isNameChanged == true);
+
sh->setName(std::wstring(NewDisplayName));
sh->getVolumeInfo()->isNameChanged = true;
return S_OK;
@@ -238,13 +239,15 @@ bool Session::fetchNameViaFD(std::wstring exePath, DWORD pid, std::wstring *sess
void* fileVersionInfo = malloc(fileVersionInfoSize);
if(!GetFileVersionInfoExW(FILE_VER_GET_LOCALISED | FILE_VER_GET_NEUTRAL,
- exePath.c_str(),0,fileVersionInfoSize, fileVersionInfo))
+ exePath.c_str(),0,fileVersionInfoSize, fileVersionInfo)) {
return false;
+ }
UINT translationArrayLen = 0;
- if (!VerQueryValueW(fileVersionInfo, L"\\VarFileInfo\\Translation", (LPVOID*)&translationArray, &translationArrayLen))
+ if (!VerQueryValueW(fileVersionInfo, L"\\VarFileInfo\\Translation", (LPVOID*)&translationArray, &translationArrayLen)) {
+ free(fileVersionInfo);
return false;
-
+ }
//File descriptor parsing
//TODO: https://learn.microsoft.com/en-us/windows/win32/api/winnls/nf-winnls-getuserpreferreduilanguages
/* It is possible to retrieve user languages and try to use one of those before falling back to whatever
@@ -257,7 +260,6 @@ bool Session::fetchNameViaFD(std::wstring exePath, DWORD pid, std::wstring *sess
int8_t syslangIdx = -1;
wchar_t metadataStringKey[256];
wchar_t* metadataString = NULL;
- //std::wstring name;
for (UINT i = 0; i < availableLangs; i++) {
LANGID defaultUILanguage = GetUserDefaultUILanguage();
if (defaultUILanguage != translationArray[i].wLanguage)
@@ -340,8 +342,6 @@ bool Session::fetchNameViaMSIX(std::wstring exePath, DWORD pid, std::wstring *se
if (sessionName->length() > 0)
return true;
else return false;
-
-
}
@@ -419,4 +419,7 @@ Session::~Session() {
meterInformation->Release();
sessionControl->Release();
sessionVolume->Release();
+ meterInformation = nullptr;
+ sessionControl = nullptr;
+ sessionVolume = nullptr;
}
diff --git a/src/cont/contclasses.cpp b/src/cont/contclasses.cpp
index 27ae4a6..d2188f0 100644
--- a/src/cont/contclasses.cpp
+++ b/src/cont/contclasses.cpp
@@ -28,7 +28,6 @@ EndpointHandler::EndpointHandler(uint64_t idx, Flows flow) {
this->flow = flow;
this->ep = (flow == Flows::FLOW_PLAYBACK ? osh->getPlaybackEndpoints().at(idx) : osh->getCaptureEndpoints().at(idx));
- epc = new EndpointVolumeCallback(ep);
this->callbackInfo.caller = osh->getGuid();
//epName = ep->getName();
this->setBackEndpointVolumeCallbackInfoContent(this->getState());
@@ -115,7 +114,10 @@ void EndpointHandler::setBackEndpointVolumeCallbackInfoContent(uint8_t state) {
callbackInfo.muted = this->getMute();
callbackInfo.mainVolume = this->getVolume(AudioChannel::CHANNEL_MAIN);
callbackInfo.channels = this->getChannelCount();
- ep->setVolumeCallback(epc);
+ if (!epc) {
+ epc = new EndpointVolumeCallback(ep);
+ ep->setVolumeCallback(epc);
+ }
callbackInfo.channelVolumes.resize(this->callbackInfo.channels);
for(uint32_t i = 0; i < this->getChannelCount(); i++){
callbackInfo.channelVolumes.at(i) = this->getVolume(i);
@@ -181,13 +183,13 @@ Endpoint* EndpointHandler::getEndpoint() {
void EndpointHandler::addSessionSendFront(Session* session) {
if(!ep->endpointSessionsMutex.try_lock()) return;
+ sessionHandlersMutex.lock();
this->ep->addSession(session);
SessionHandler* sessionHandler = new SessionHandler(this, session, (getSessionCount() - 1));
- ep->endpointSessionsMutex.unlock();
- sessionHandlersMutex.lock();
sessionHandlers.push_back(sessionHandler);
+ ep->endpointSessionsMutex.unlock();
sessionHandlersMutex.unlock();
this->addSessionWidget(sessionHandler);
}
@@ -203,11 +205,13 @@ void EndpointHandler::removeSessionFromFront(SessionHandler* sh) {
void EndpointHandler::deleteSessions() {
ep->unregisterNewSessionNotification(ensc);
ensc->Release();
+ ensc = nullptr;
for (auto sh : sessionHandlers) {
delete sh;
}
sessionHandlers.resize(0);
ep->deleteSessions();
+ //ep->deleteSessionManager();
}
void EndpointHandler::createSessionHandlers() {
@@ -218,6 +222,9 @@ void EndpointHandler::createSessionHandlers() {
sessionHandlers.push_back(sessionHandler);
}
}
+}
+
+void EndpointHandler::createSessionHandlersCallback() {
ensc = new EndpointNewSessionCallback(this);
ep->registerNewSessionNotification(ensc);
}
@@ -232,6 +239,12 @@ void EndpointHandler::unlockSessionCollections() {
this->ep->endpointSessionsMutex.unlock();
}
+void EndpointHandler::removeVolumeCallback() {
+ ep->removeVolumeCallback(epc);
+ epc->Release();
+ epc = nullptr;
+}
+
EndpointHandler::~EndpointHandler() {
ep->removeVolumeCallback(epc);
ep->unregisterNewSessionNotification(ensc);
@@ -411,10 +424,13 @@ void OverseerHandler::reviseEndpointShowing(std::wstring endpointId, EndpointSta
if (flow == Flows::FLOW_CAPTURE) goto end;
if (eph && EndpointState::ENDPOINT_ACTIVE & state) {
+ eph->setState(EndpointState::ENDPOINT_ACTIVE);
this->addEndpointWidget(eph);
} else if (eph && eph->getFrontVisibilityState() == EndpointState::ENDPOINT_ACTIVE) {
+ eph->removeVolumeCallback();
this->removeEndpointWidget(eph->getFrontVisibilityIndex());
}
+
end:
handlersPlaybackMutex.unlock();
handlersCaptureMutex.unlock();
diff --git a/src/cont/contclasses.h b/src/cont/contclasses.h
index 2034986..cd0e03e 100644
--- a/src/cont/contclasses.h
+++ b/src/cont/contclasses.h
@@ -76,9 +76,11 @@ public:
void removeSessionFromFront(SessionHandler* sh);
void deleteSessions();
void createSessionHandlers();
+ void createSessionHandlersCallback();
std::mutex sessionHandlersMutex;
void lockSessionCollections();
void unlockSessionCollections();
+ void removeVolumeCallback();
~EndpointHandler();
private:
diff --git a/src/cont/contsessionclasses.h b/src/cont/contsessionclasses.h
index 455bbe1..24c0ba7 100644
--- a/src/cont/contsessionclasses.h
+++ b/src/cont/contsessionclasses.h
@@ -9,10 +9,10 @@ class SessionStateCallback;
struct SessionVolumeInfo {
//SessionVolumeInfo(bool muted, float mainVolume);
- bool muted;
- float mainVolume;
- NGuid caller;
- bool isNameChanged = false;
+ bool muted;
+ float mainVolume;
+ NGuid caller;
+ std::atomic isNameChanged = false;
//size_t channels;
//std::vector channelVolumes;
};
diff --git a/src/qt/qtclasses.cpp b/src/qt/qtclasses.cpp
index 2e2a4b0..a9148b9 100644
--- a/src/qt/qtclasses.cpp
+++ b/src/qt/qtclasses.cpp
@@ -207,6 +207,36 @@ void ExtendedCheckBox::customEvent(QEvent* ev) {
QCheckBox::customEvent(ev);
}
+void ExtendedCheckBox::paintEvent(QPaintEvent *event) {
+ QStylePainter p(this);
+ QStyleOptionButton opt;
+ initStyleOption(&opt);
+ opt.icon = this->icons;
+ p.drawControl((QStyle::ControlElement)CustomControlElement::CE_ExtendedCheckBox, opt);
+ //QStyle* style = QApplication::style();
+ //style->drawComplexControl((QStyle::ComplexControl)CC_MeterSlider, &sliderComplex2, &painter, this);
+}
+
+void ExtendedCheckBox::addIcon(char* const path, QIcon::State state) {
+ QString str(path);
+ QSvgRenderer rr(str);
+ QPixmap pixmap(64, 64);
+ pixmap.fill(Qt::transparent);
+ QPainter painter(&pixmap);
+ rr.render(&painter);
+ painter.setCompositionMode(QPainter::CompositionMode_SourceIn);
+ uint8_t a, r, g, b;
+ if (StylingHelper::argbToDiscreteValues(osh->getAccentColor(), &r, &g, &b, &a)) {
+ QColor color(r, g, b, a);
+ painter.fillRect(pixmap.rect(), color);
+ }
+ painter.end();
+ icons.addPixmap(pixmap, QIcon::Normal, state);
+ //this->setIcon(icons);
+ //icons.addFile(":/Icons/images/second.svg",QSize(32,32),QIcon::Normal,QIcon::Off);
+}
+
+
QRect MainWindow::setSizePosition(QScreen* screen, int width, int height) {
//setGeometry ignores decoration size, theres others for that
QRect trayIconPos = this->trayIcon->geometry();
@@ -276,7 +306,7 @@ void MainWindow::compose(bool isVisible) {
* Setting correct widget widths and heights
*/
log_to_file("[Compose]\n");
- screen = this->getCurrentScreen();
+ screen = StylingHelper::getCurrentScreen();
log_debugcpp("Screen: " + screen->model().toStdString() + " " + screen->name().toStdString());
QRect screenRes = screen->geometry();
@@ -336,7 +366,7 @@ void MainWindow::compose(bool isVisible) {
}
QScreen* MainWindow::getCurrentScreen() {
- //todo: Using cursor pos as screen detector. Flawed.
+ //note: Using cursor pos as screen detector. Flawed.
QPoint cursorPos = QCursor::pos();
log_debugcpp("Cursor pos: " + std::to_string(cursorPos.ry()) + " " + std::to_string(cursorPos.rx()));
@@ -363,7 +393,9 @@ SessionWidget::SessionWidget(uint64_t idx, SessionHandler* sh, QWidget *parent)
widgetLayout->getContentsMargins(&left, &top, &right, &bottom);
widgetLayout->setContentsMargins(0, top, 0, bottom);
- muteButton = new QCheckBox(this);
+ muteButton = new ExtendedCheckBox(this);
+ muteButton->addIcon(":/assets/mute.svg", QIcon::On);
+ muteButton->addIcon(":/assets/unmute.svg", QIcon::Off);
mainLabel = new QLabel(QString::fromStdWString(sh->getName()), this);
mainLabel->setToolTip(QString::fromStdWString(sh->getName()));
mainSlider = new MeterSlider(Qt::Horizontal, this);
@@ -411,7 +443,8 @@ SessionWidget::SessionWidget(uint64_t idx, SessionHandler* sh, QWidget *parent)
if (sh->getVolumeInfo()->isNameChanged) {
mainLabel->setText(QString::fromStdWString(sh->getName()));
mainLabel->setToolTip(QString::fromStdWString(sh->getName()));
- }
+ sh->getVolumeInfo()->isNameChanged = false;
+ }
const float roundingFactor = 0.005;
mainSlider->blockSignals(true);
muteButton->blockSignals(true);
@@ -538,7 +571,7 @@ EndpointWidget::EndpointWidget(EndpointHandler* eph, QWidget *parent, uint64_t i
eph->createSessionHandlers();
//todo: sussy
- this->eph->setState(EndpointState::ENDPOINT_ACTIVE, idx);
+ this->eph->setFrontVisibilityInfo(EndpointState::ENDPOINT_ACTIVE, idx);
this->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed);
widgetLayout = new QGridLayout(this);
//this->setContentsMargins(0, 0, 0, 0);
@@ -698,9 +731,9 @@ EndpointWidget::EndpointWidget(EndpointHandler* eph, QWidget *parent, uint64_t i
});
log_debugcpp("ENDPOINT_WIDGETED");
+ eph->createSessionHandlersCallback();
}
-
void EndpointWidget::addSessionWidget(CustomWidgetEvent* ev){
this->setUpdatesEnabled(false);
uint64_t index = this->sessionWidgets.size();
@@ -779,7 +812,7 @@ void MainWindow::customEvent(QEvent* ev) {
}
//__attribute__((optimize("O0", "unroll-loops")))
-void MainWindow::removeEndpointWidget(CustomWidgetEvent* ev){
+void MainWindow::removeEndpointWidget(CustomWidgetEvent* ev) {
uint64_t i = ev->payload;
this->ews.at(i)->setParent(nullptr);
this->widgetLayout->removeWidget(ews.at(i));
@@ -792,7 +825,7 @@ void MainWindow::removeEndpointWidget(CustomWidgetEvent* ev){
return;
}
-void MainWindow::addEndpointWidget(CustomWidgetEvent* ev){
+void MainWindow::addEndpointWidget(CustomWidgetEvent* ev) {
EndpointWidget* epw = new EndpointWidget(ev->payload, containerWidget, this->ews.size());
//epw->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
epw->setParent(this);
@@ -924,9 +957,9 @@ EndpointHandler* EndpointWidget::getEndpointHandler(){
* }
*/
-void EndpointWidget::setIndex(uint64_t idx){
+void EndpointWidget::setIndex(uint64_t idx) {
this->idx = idx;
- this->eph->setState(EndpointState::ENDPOINT_ACTIVE, this->idx);
+ this->eph->setFrontVisibilityInfo(EndpointState::ENDPOINT_ACTIVE, this->idx);
}
uint64_t EndpointWidget::getIndex(){
diff --git a/src/qt/qtclasses.h b/src/qt/qtclasses.h
index 0d5733f..1b2658b 100644
--- a/src/qt/qtclasses.h
+++ b/src/qt/qtclasses.h
@@ -40,12 +40,17 @@ public:
class ExtendedCheckBox : public QCheckBox {
Q_OBJECT
+private:
+ QIcon icons;
+
protected:
void customEvent(QEvent* ev) override;
+ void paintEvent(QPaintEvent* event) override;
public:
//c++11: this inherits all parent's constructors unconditionally
using QCheckBox::QCheckBox;
+ void addIcon(char* const path, QIcon::State state);
//alternative being calling parent ctor directly after declaring child ctor:
//B(int x) : A(x) { }
};
@@ -68,7 +73,7 @@ private:
MeterSlider *mainSlider = nullptr;
uint64_t idx;
QHBoxLayout *widgetLayout = nullptr;
- QCheckBox *muteButton = nullptr;
+ ExtendedCheckBox *muteButton = nullptr;
SessionHandler* sh;
QTimer* volumePoller = nullptr;
QSpacerItem* widthSpacer;
diff --git a/src/qt/qtcommon.h b/src/qt/qtcommon.h
index b059504..61873eb 100644
--- a/src/qt/qtcommon.h
+++ b/src/qt/qtcommon.h
@@ -44,6 +44,7 @@
#include
#include
#include
+#include
//#include
//#include
/*
@@ -63,6 +64,10 @@ enum CustomComplexControl {
CC_MeterSlider = 0xf0000001
};
+enum CustomControlElement {
+ CE_ExtendedCheckBox = 0xf0000001
+};
+
namespace StylingHelper {
static inline void setBackgroundColor(bool lightMode) {
@@ -218,5 +223,32 @@ namespace StylingHelper {
return pal.color(QPalette::Base);
}
+ static inline QPixmap svg2Pixmap(const QString& svgContent,
+ const QSize& size,
+ QPainter::CompositionMode mode = QPainter::CompositionMode_SourceOver)
+ {
+ QSvgRenderer rr(svgContent);
+ QImage image(size.width(), size.height(), QImage::Format_ARGB32);
+ QPainter painter(&image);
+ painter.setCompositionMode(mode);
+ image.fill(Qt::transparent);
+ rr.render(&painter);
+ return QPixmap::fromImage(image);
+ }
+
+ static inline QScreen* getCurrentScreen() {
+ //note: Using cursor pos as screen detector. Flawed.
+ QPoint cursorPos = QCursor::pos();
+
+ for (QScreen *screen : QGuiApplication::screens()) {
+ QRect screenRect = screen->geometry();
+ if (screenRect.contains(cursorPos)) {
+ return screen;
+ }
+ }
+
+ return QGuiApplication::primaryScreen();
+ }
+
}
diff --git a/src/qt/qtvisuals.h b/src/qt/qtvisuals.h
index 0db58e8..8902ed1 100644
--- a/src/qt/qtvisuals.h
+++ b/src/qt/qtvisuals.h
@@ -20,8 +20,10 @@ public:
return baseStyle()->styleHint(hint, option, widget, returnData);
}
- QRect subControlRect(ComplexControl control, const QStyleOptionComplex *option,
- SubControl subControl, const QWidget *widget) const {
+ QRect subControlRect(ComplexControl control,
+ const QStyleOptionComplex *option,
+ SubControl subControl,
+ const QWidget *widget) const {
QRect rect = QCommonStyle::subControlRect(CC_Slider, option, subControl, widget);
switch (control) {
@@ -186,6 +188,51 @@ public:
}
+ void drawControl(ControlElement element, const QStyleOption *opt,
+ QPainter *p, const QWidget *widget) const
+ {
+ switch(element) {
+ case CE_ExtendedCheckBox:
+ if (const QStyleOptionButton *btn = qstyleoption_cast(opt)) {
+ QStyleOptionButton subopt = *btn;
+ subopt.rect = subElementRect(SE_CheckBoxIndicator, btn, widget);
+ //proxy()->drawPrimitive(PE_IndicatorCheckBox, &subopt, p, widget);
+ subopt.rect = subElementRect(SE_CheckBoxContents, btn, widget);
+
+ //proxy()->drawControl(CE_CheckBoxLabel, &subopt, p, widget);
+ int alignment = visualAlignment(btn->direction, Qt::AlignLeft | Qt::AlignVCenter);
+
+ if (!proxy()->styleHint(SH_UnderlineShortcut, btn, widget))
+ alignment |= Qt::TextHideMnemonic;
+ QPixmap pix;
+ QRect textRect = btn->rect;
+ if (!btn->icon.isNull()) {
+ pix = btn->icon.pixmap(btn->iconSize, StylingHelper::getCurrentScreen()->devicePixelRatio(),
+ QIcon::Mode::Normal, btn->state & State_On ? QIcon::On : QIcon::Off);
+ proxy()->drawItemPixmap(p, btn->rect, alignment, pix);
+ if (btn->direction == Qt::RightToLeft)
+ textRect.setRight(textRect.right() - btn->iconSize.width() - 4);
+ else
+ textRect.setLeft(textRect.left() + btn->iconSize.width() + 4);
+ }
+ if (!btn->text.isEmpty()){
+ proxy()->drawItemText(p, textRect, alignment | Qt::TextShowMnemonic,
+ btn->palette, btn->state & State_Enabled, btn->text, QPalette::WindowText);
+ }
+ //
+ if (btn->state & State_HasFocus) {
+ QStyleOptionFocusRect fropt;
+ fropt.QStyleOption::operator=(*btn);
+ fropt.rect = subElementRect(SE_CheckBoxFocusRect, btn, widget);
+ proxy()->drawPrimitive(PE_FrameFocusRect, &fropt, p, widget);
+ }
+ }
+ default:
+ baseStyle()->drawControl(element, opt, p, widget);
+ break;
+ }
+ }
+
void drawComplexControl(ComplexControl cc, const QStyleOptionComplex *option,
QPainter *painter, const QWidget *widget) const {
QColor outline;