From 99fce0dce0f9018c548c80aa0fc672c89123df81 Mon Sep 17 00:00:00 2001
From: Jon Beniston <jon@beniston.com>
Date: Fri, 22 Apr 2022 18:21:24 +0100
Subject: [PATCH] Add FramelessWindowResizer class for resizig frameless
 windows. Remove top-right resize grip from windows. In channels and features,
 make sure enterEvent and leaveEvent are passed to parent class.

---
 .../channelrx/chanalyzer/chanalyzergui.cpp    |   6 +-
 plugins/channelrx/demodadsb/adsbdemodgui.cpp  |   6 +-
 plugins/channelrx/demodais/aisdemodgui.cpp    |   6 +-
 plugins/channelrx/demodam/amdemodgui.cpp      |   6 +-
 plugins/channelrx/demodapt/aptdemodgui.cpp    |   6 +-
 plugins/channelrx/demodatv/atvdemodgui.cpp    |   6 +-
 plugins/channelrx/demodbfm/bfmdemodgui.cpp    |   6 +-
 plugins/channelrx/demoddab/dabdemodgui.cpp    |   6 +-
 plugins/channelrx/demoddatv/datvdemodgui.cpp  |   6 +-
 plugins/channelrx/demoddsd/dsddemodgui.cpp    |   6 +-
 .../channelrx/demodfreedv/freedvdemodgui.cpp  |   6 +-
 plugins/channelrx/demodnfm/nfmdemodgui.cpp    |   6 +-
 .../channelrx/demodpacket/packetdemodgui.cpp  |   6 +-
 .../channelrx/demodpager/pagerdemodgui.cpp    |   6 +-
 .../demodradiosonde/radiosondedemodgui.cpp    |   6 +-
 plugins/channelrx/demodssb/ssbdemodgui.cpp    |   6 +-
 plugins/channelrx/demodvor/vordemodgui.cpp    |   6 +-
 .../channelrx/demodvorsc/vordemodscgui.cpp    |   6 +-
 plugins/channelrx/demodwfm/wfmdemodgui.cpp    |   6 +-
 plugins/channelrx/filesink/filesinkgui.cpp    |   6 +-
 .../channelrx/freqtracker/freqtrackergui.cpp  |   6 +-
 plugins/channelrx/localsink/localsinkgui.cpp  |   6 +-
 .../channelrx/noisefigure/noisefiguregui.cpp  |   6 +-
 .../radioastronomy/radioastronomygui.cpp      |   6 +-
 .../channelrx/radioclock/radioclockgui.cpp    |   6 +-
 .../channelrx/remotesink/remotesinkgui.cpp    |   6 +-
 .../sigmffilesink/sigmffilesinkgui.cpp        |   6 +-
 plugins/channelrx/udpsink/udpsinkgui.cpp      |   6 +-
 .../channeltx/filesource/filesourcegui.cpp    |   6 +-
 .../channeltx/localsource/localsourcegui.cpp  |   6 +-
 .../mod802.15.4/ieee_802_15_4_modgui.cpp      |   6 +-
 plugins/channeltx/modais/aismodgui.cpp        |   6 +-
 plugins/channeltx/modam/ammodgui.cpp          |   6 +-
 plugins/channeltx/modatv/atvmodgui.cpp        |   6 +-
 .../modchirpchat/chirpchatmodgui.cpp          |   6 +-
 plugins/channeltx/moddatv/datvmodgui.cpp      |   6 +-
 plugins/channeltx/modfreedv/freedvmodgui.cpp  |   6 +-
 plugins/channeltx/modnfm/nfmmodgui.cpp        |   6 +-
 plugins/channeltx/modpacket/packetmodgui.cpp  |   6 +-
 plugins/channeltx/modssb/ssbmodgui.cpp        |   6 +-
 plugins/channeltx/modwfm/wfmmodgui.cpp        |   6 +-
 .../remotesource/remotesourcegui.cpp          |   6 +-
 plugins/channeltx/udpsource/udpsourcegui.cpp  |   6 +-
 plugins/feature/afc/afcgui.cpp                |   8 -
 plugins/feature/afc/afcgui.h                  |   3 -
 plugins/feature/ais/aisgui.cpp                |   8 -
 plugins/feature/ais/aisgui.h                  |   3 -
 .../feature/antennatools/antennatoolsgui.cpp  |   8 -
 .../feature/antennatools/antennatoolsgui.h    |   3 -
 plugins/feature/aprs/aprsgui.cpp              |   8 -
 plugins/feature/aprs/aprsgui.h                |   3 -
 .../demodanalyzer/demodanalyzergui.cpp        |   8 -
 .../feature/demodanalyzer/demodanalyzergui.h  |   3 -
 .../gs232controller/gs232controllergui.cpp    |   8 -
 .../gs232controller/gs232controllergui.h      |   3 -
 .../jogdialcontrollergui.cpp                  |   8 -
 .../jogdialcontroller/jogdialcontrollergui.h  |   3 -
 plugins/feature/map/mapgui.cpp                |   8 -
 plugins/feature/map/mapgui.h                  |   3 -
 plugins/feature/pertester/pertestergui.cpp    |   8 -
 plugins/feature/pertester/pertestergui.h      |   3 -
 plugins/feature/radiosonde/radiosondegui.cpp  |   8 -
 plugins/feature/radiosonde/radiosondegui.h    |   3 -
 .../feature/rigctlserver/rigctlservergui.cpp  |   8 -
 .../feature/rigctlserver/rigctlservergui.h    |   3 -
 .../satellitetracker/satellitetrackergui.cpp  |   8 -
 .../satellitetracker/satellitetrackergui.h    |   3 -
 plugins/feature/simpleptt/simplepttgui.cpp    |   8 -
 plugins/feature/simpleptt/simplepttgui.h      |   3 -
 .../feature/startracker/startrackergui.cpp    |   8 -
 plugins/feature/startracker/startrackergui.h  |   3 -
 .../feature/vorlocalizer/vorlocalizergui.cpp  |   8 -
 .../feature/vorlocalizer/vorlocalizergui.h    |   3 -
 sdrgui/CMakeLists.txt                         |   2 +
 sdrgui/channel/channelgui.cpp                 |  30 ++-
 sdrgui/channel/channelgui.h                   |  11 +-
 sdrgui/device/devicegui.cpp                   |  30 ++-
 sdrgui/device/devicegui.h                     |  11 +-
 sdrgui/feature/featuregui.cpp                 |  30 ++-
 sdrgui/feature/featuregui.h                   |  11 +-
 sdrgui/gui/framelesswindowresizer.cpp         | 236 ++++++++++++++++++
 sdrgui/gui/framelesswindowresizer.h           |  70 ++++++
 sdrgui/mainspectrum/mainspectrumgui.cpp       |  30 ++-
 sdrgui/mainspectrum/mainspectrumgui.h         |  14 +-
 84 files changed, 599 insertions(+), 299 deletions(-)
 create mode 100644 sdrgui/gui/framelesswindowresizer.cpp
 create mode 100644 sdrgui/gui/framelesswindowresizer.h

diff --git a/plugins/channelrx/chanalyzer/chanalyzergui.cpp b/plugins/channelrx/chanalyzer/chanalyzergui.cpp
index 4be9991f5..aa5289e1e 100644
--- a/plugins/channelrx/chanalyzer/chanalyzergui.cpp
+++ b/plugins/channelrx/chanalyzer/chanalyzergui.cpp
@@ -696,14 +696,16 @@ void ChannelAnalyzerGUI::applySettings(bool force)
 	}
 }
 
-void ChannelAnalyzerGUI::leaveEvent(QEvent*)
+void ChannelAnalyzerGUI::leaveEvent(QEvent* event)
 {
 	m_channelMarker.setHighlighted(false);
+    ChannelGUI::leaveEvent(event);
 }
 
-void ChannelAnalyzerGUI::enterEvent(QEvent*)
+void ChannelAnalyzerGUI::enterEvent(QEvent* event)
 {
 	m_channelMarker.setHighlighted(true);
+    ChannelGUI::enterEvent(event);
 }
 
 void ChannelAnalyzerGUI::makeUIConnections()
diff --git a/plugins/channelrx/demodadsb/adsbdemodgui.cpp b/plugins/channelrx/demodadsb/adsbdemodgui.cpp
index e8dca5747..4005e67a6 100644
--- a/plugins/channelrx/demodadsb/adsbdemodgui.cpp
+++ b/plugins/channelrx/demodadsb/adsbdemodgui.cpp
@@ -4005,14 +4005,16 @@ void ADSBDemodGUI::displaySettings()
     blockApplySettings(false);
 }
 
-void ADSBDemodGUI::leaveEvent(QEvent*)
+void ADSBDemodGUI::leaveEvent(QEvent* event)
 {
     m_channelMarker.setHighlighted(false);
+    ChannelGUI::leaveEvent(event);
 }
 
-void ADSBDemodGUI::enterEvent(QEvent*)
+void ADSBDemodGUI::enterEvent(QEvent* event)
 {
     m_channelMarker.setHighlighted(true);
+    ChannelGUI::enterEvent(event);
 }
 
 void ADSBDemodGUI::blockApplySettings(bool block)
diff --git a/plugins/channelrx/demodais/aisdemodgui.cpp b/plugins/channelrx/demodais/aisdemodgui.cpp
index 2a70598f8..c1095e8f5 100644
--- a/plugins/channelrx/demodais/aisdemodgui.cpp
+++ b/plugins/channelrx/demodais/aisdemodgui.cpp
@@ -635,14 +635,16 @@ void AISDemodGUI::displaySettings()
     blockApplySettings(false);
 }
 
-void AISDemodGUI::leaveEvent(QEvent*)
+void AISDemodGUI::leaveEvent(QEvent* event)
 {
     m_channelMarker.setHighlighted(false);
+    ChannelGUI::leaveEvent(event);
 }
 
-void AISDemodGUI::enterEvent(QEvent*)
+void AISDemodGUI::enterEvent(QEvent* event)
 {
     m_channelMarker.setHighlighted(true);
+    ChannelGUI::enterEvent(event);
 }
 
 void AISDemodGUI::tick()
diff --git a/plugins/channelrx/demodam/amdemodgui.cpp b/plugins/channelrx/demodam/amdemodgui.cpp
index 35641d3e9..a50a2ef5c 100644
--- a/plugins/channelrx/demodam/amdemodgui.cpp
+++ b/plugins/channelrx/demodam/amdemodgui.cpp
@@ -396,14 +396,16 @@ void AMDemodGUI::displaySettings()
     blockApplySettings(false);
 }
 
-void AMDemodGUI::leaveEvent(QEvent*)
+void AMDemodGUI::leaveEvent(QEvent* event)
 {
 	m_channelMarker.setHighlighted(false);
+    ChannelGUI::leaveEvent(event);
 }
 
-void AMDemodGUI::enterEvent(QEvent*)
+void AMDemodGUI::enterEvent(QEvent* event)
 {
 	m_channelMarker.setHighlighted(true);
+    ChannelGUI::enterEvent(event);
 }
 
 void AMDemodGUI::audioSelect()
diff --git a/plugins/channelrx/demodapt/aptdemodgui.cpp b/plugins/channelrx/demodapt/aptdemodgui.cpp
index 19e0b08c7..f7889956a 100644
--- a/plugins/channelrx/demodapt/aptdemodgui.cpp
+++ b/plugins/channelrx/demodapt/aptdemodgui.cpp
@@ -814,14 +814,16 @@ void APTDemodGUI::displayPalettes()
     ui->channels->blockSignals(false);
 }
 
-void APTDemodGUI::leaveEvent(QEvent*)
+void APTDemodGUI::leaveEvent(QEvent* event)
 {
     m_channelMarker.setHighlighted(false);
+    ChannelGUI::leaveEvent(event);
 }
 
-void APTDemodGUI::enterEvent(QEvent*)
+void APTDemodGUI::enterEvent(QEvent* event)
 {
     m_channelMarker.setHighlighted(true);
+    ChannelGUI::enterEvent(event);
 }
 
 void APTDemodGUI::tick()
diff --git a/plugins/channelrx/demodatv/atvdemodgui.cpp b/plugins/channelrx/demodatv/atvdemodgui.cpp
index 21cda893d..bc0ca5eab 100644
--- a/plugins/channelrx/demodatv/atvdemodgui.cpp
+++ b/plugins/channelrx/demodatv/atvdemodgui.cpp
@@ -407,14 +407,16 @@ void ATVDemodGUI::setRFFiltersSlidersRange(int sampleRate)
     ui->rfOppBWText->setText(QString("%1k").arg((ui->rfOppBW->value() * m_rfSliderDivisor) / 1000.0, 0, 'f', 0));
 }
 
-void ATVDemodGUI::leaveEvent(QEvent*)
+void ATVDemodGUI::leaveEvent(QEvent* event)
 {
     m_channelMarker.setHighlighted(false);
+    ChannelGUI::leaveEvent(event);
 }
 
-void ATVDemodGUI::enterEvent(QEvent*)
+void ATVDemodGUI::enterEvent(QEvent* event)
 {
     m_channelMarker.setHighlighted(true);
+    ChannelGUI::enterEvent(event);
 }
 
 void ATVDemodGUI::tick()
diff --git a/plugins/channelrx/demodbfm/bfmdemodgui.cpp b/plugins/channelrx/demodbfm/bfmdemodgui.cpp
index 02fc49f35..9917db345 100644
--- a/plugins/channelrx/demodbfm/bfmdemodgui.cpp
+++ b/plugins/channelrx/demodbfm/bfmdemodgui.cpp
@@ -500,14 +500,16 @@ void BFMDemodGUI::displaySettings()
     blockApplySettings(false);
 }
 
-void BFMDemodGUI::leaveEvent(QEvent*)
+void BFMDemodGUI::leaveEvent(QEvent* event)
 {
 	m_channelMarker.setHighlighted(false);
+    ChannelGUI::leaveEvent(event);
 }
 
-void BFMDemodGUI::enterEvent(QEvent*)
+void BFMDemodGUI::enterEvent(QEvent* event)
 {
 	m_channelMarker.setHighlighted(true);
+    ChannelGUI::enterEvent(event);
 }
 
 void BFMDemodGUI::audioSelect()
diff --git a/plugins/channelrx/demoddab/dabdemodgui.cpp b/plugins/channelrx/demoddab/dabdemodgui.cpp
index cee8f38a5..dd8e36f21 100644
--- a/plugins/channelrx/demoddab/dabdemodgui.cpp
+++ b/plugins/channelrx/demoddab/dabdemodgui.cpp
@@ -588,14 +588,16 @@ void DABDemodGUI::displaySettings()
     blockApplySettings(false);
 }
 
-void DABDemodGUI::leaveEvent(QEvent*)
+void DABDemodGUI::leaveEvent(QEvent* event)
 {
     m_channelMarker.setHighlighted(false);
+    ChannelGUI::leaveEvent(event);
 }
 
-void DABDemodGUI::enterEvent(QEvent*)
+void DABDemodGUI::enterEvent(QEvent* event)
 {
     m_channelMarker.setHighlighted(true);
+    ChannelGUI::enterEvent(event);
 }
 
 void DABDemodGUI::clearProgram()
diff --git a/plugins/channelrx/demoddatv/datvdemodgui.cpp b/plugins/channelrx/demoddatv/datvdemodgui.cpp
index c2ed28dae..eeb480588 100644
--- a/plugins/channelrx/demoddatv/datvdemodgui.cpp
+++ b/plugins/channelrx/demoddatv/datvdemodgui.cpp
@@ -479,18 +479,20 @@ void DATVDemodGUI::applySettings(bool force)
     }
 }
 
-void DATVDemodGUI::leaveEvent(QEvent*)
+void DATVDemodGUI::leaveEvent(QEvent* event)
 {
     blockApplySettings(true);
     m_channelMarker.setHighlighted(false);
     blockApplySettings(false);
+    ChannelGUI::leaveEvent(event);
 }
 
-void DATVDemodGUI::enterEvent(QEvent*)
+void DATVDemodGUI::enterEvent(QEvent* event)
 {
     blockApplySettings(true);
     m_channelMarker.setHighlighted(true);
     blockApplySettings(false);
+    ChannelGUI::enterEvent(event);
 }
 
 void DATVDemodGUI::audioSelect()
diff --git a/plugins/channelrx/demoddsd/dsddemodgui.cpp b/plugins/channelrx/demoddsd/dsddemodgui.cpp
index 201d95bd0..3b0ee3fae 100644
--- a/plugins/channelrx/demoddsd/dsddemodgui.cpp
+++ b/plugins/channelrx/demoddsd/dsddemodgui.cpp
@@ -497,14 +497,16 @@ void DSDDemodGUI::applySettings(bool force)
 	}
 }
 
-void DSDDemodGUI::leaveEvent(QEvent*)
+void DSDDemodGUI::leaveEvent(QEvent* event)
 {
 	m_channelMarker.setHighlighted(false);
+    ChannelGUI::leaveEvent(event);
 }
 
-void DSDDemodGUI::enterEvent(QEvent*)
+void DSDDemodGUI::enterEvent(QEvent* event)
 {
 	m_channelMarker.setHighlighted(true);
+    ChannelGUI::enterEvent(event);
 }
 
 void DSDDemodGUI::blockApplySettings(bool block)
diff --git a/plugins/channelrx/demodfreedv/freedvdemodgui.cpp b/plugins/channelrx/demodfreedv/freedvdemodgui.cpp
index 0e46817b6..6ea0372a8 100644
--- a/plugins/channelrx/demodfreedv/freedvdemodgui.cpp
+++ b/plugins/channelrx/demodfreedv/freedvdemodgui.cpp
@@ -414,14 +414,16 @@ void FreeDVDemodGUI::displaySettings()
     blockApplySettings(false);
 }
 
-void FreeDVDemodGUI::leaveEvent(QEvent*)
+void FreeDVDemodGUI::leaveEvent(QEvent* event)
 {
 	m_channelMarker.setHighlighted(false);
+    ChannelGUI::leaveEvent(event);
 }
 
-void FreeDVDemodGUI::enterEvent(QEvent*)
+void FreeDVDemodGUI::enterEvent(QEvent* event)
 {
 	m_channelMarker.setHighlighted(true);
+    ChannelGUI::enterEvent(event);
 }
 
 void FreeDVDemodGUI::audioSelect()
diff --git a/plugins/channelrx/demodnfm/nfmdemodgui.cpp b/plugins/channelrx/demodnfm/nfmdemodgui.cpp
index a6e670963..ebf9628d3 100644
--- a/plugins/channelrx/demodnfm/nfmdemodgui.cpp
+++ b/plugins/channelrx/demodnfm/nfmdemodgui.cpp
@@ -532,14 +532,16 @@ void NFMDemodGUI::displaySettings()
     blockApplySettings(false);
 }
 
-void NFMDemodGUI::leaveEvent(QEvent*)
+void NFMDemodGUI::leaveEvent(QEvent* event)
 {
 	m_channelMarker.setHighlighted(false);
+    ChannelGUI::leaveEvent(event);
 }
 
-void NFMDemodGUI::enterEvent(QEvent*)
+void NFMDemodGUI::enterEvent(QEvent* event)
 {
 	m_channelMarker.setHighlighted(true);
+    ChannelGUI::enterEvent(event);
 }
 
 void NFMDemodGUI::setCtcssFreq(Real ctcssFreq)
diff --git a/plugins/channelrx/demodpacket/packetdemodgui.cpp b/plugins/channelrx/demodpacket/packetdemodgui.cpp
index afae8ef80..28bbf01db 100644
--- a/plugins/channelrx/demodpacket/packetdemodgui.cpp
+++ b/plugins/channelrx/demodpacket/packetdemodgui.cpp
@@ -569,14 +569,16 @@ void PacketDemodGUI::displaySettings()
     blockApplySettings(false);
 }
 
-void PacketDemodGUI::leaveEvent(QEvent*)
+void PacketDemodGUI::leaveEvent(QEvent* event)
 {
     m_channelMarker.setHighlighted(false);
+    ChannelGUI::leaveEvent(event);
 }
 
-void PacketDemodGUI::enterEvent(QEvent*)
+void PacketDemodGUI::enterEvent(QEvent* event)
 {
     m_channelMarker.setHighlighted(true);
+    ChannelGUI::enterEvent(event);
 }
 
 void PacketDemodGUI::tick()
diff --git a/plugins/channelrx/demodpager/pagerdemodgui.cpp b/plugins/channelrx/demodpager/pagerdemodgui.cpp
index 9989ba94b..bbab5548b 100644
--- a/plugins/channelrx/demodpager/pagerdemodgui.cpp
+++ b/plugins/channelrx/demodpager/pagerdemodgui.cpp
@@ -668,14 +668,16 @@ void PagerDemodGUI::displaySettings()
     blockApplySettings(false);
 }
 
-void PagerDemodGUI::leaveEvent(QEvent*)
+void PagerDemodGUI::leaveEvent(QEvent* event)
 {
     m_channelMarker.setHighlighted(false);
+    ChannelGUI::leaveEvent(event);
 }
 
-void PagerDemodGUI::enterEvent(QEvent*)
+void PagerDemodGUI::enterEvent(QEvent* event)
 {
     m_channelMarker.setHighlighted(true);
+    ChannelGUI::enterEvent(event);
 }
 
 void PagerDemodGUI::tick()
diff --git a/plugins/channelrx/demodradiosonde/radiosondedemodgui.cpp b/plugins/channelrx/demodradiosonde/radiosondedemodgui.cpp
index 1a76963fb..d45f552a4 100644
--- a/plugins/channelrx/demodradiosonde/radiosondedemodgui.cpp
+++ b/plugins/channelrx/demodradiosonde/radiosondedemodgui.cpp
@@ -772,14 +772,16 @@ void RadiosondeDemodGUI::displaySettings()
     blockApplySettings(false);
 }
 
-void RadiosondeDemodGUI::leaveEvent(QEvent*)
+void RadiosondeDemodGUI::leaveEvent(QEvent* event)
 {
     m_channelMarker.setHighlighted(false);
+    ChannelGUI::leaveEvent(event);
 }
 
-void RadiosondeDemodGUI::enterEvent(QEvent*)
+void RadiosondeDemodGUI::enterEvent(QEvent* event)
 {
     m_channelMarker.setHighlighted(true);
+    ChannelGUI::enterEvent(event);
 }
 
 void RadiosondeDemodGUI::tick()
diff --git a/plugins/channelrx/demodssb/ssbdemodgui.cpp b/plugins/channelrx/demodssb/ssbdemodgui.cpp
index 05eba5be5..1f4e02e73 100644
--- a/plugins/channelrx/demodssb/ssbdemodgui.cpp
+++ b/plugins/channelrx/demodssb/ssbdemodgui.cpp
@@ -622,14 +622,16 @@ void SSBDemodGUI::displayAGCThresholdGate(int value)
     ui->agcThresholdGate->setValue(dialValue);
 }
 
-void SSBDemodGUI::leaveEvent(QEvent*)
+void SSBDemodGUI::leaveEvent(QEvent* event)
 {
 	m_channelMarker.setHighlighted(false);
+    ChannelGUI::leaveEvent(event);
 }
 
-void SSBDemodGUI::enterEvent(QEvent*)
+void SSBDemodGUI::enterEvent(QEvent* event)
 {
 	m_channelMarker.setHighlighted(true);
+    ChannelGUI::enterEvent(event);
 }
 
 void SSBDemodGUI::audioSelect()
diff --git a/plugins/channelrx/demodvor/vordemodgui.cpp b/plugins/channelrx/demodvor/vordemodgui.cpp
index 5c96af2d6..18df17879 100644
--- a/plugins/channelrx/demodvor/vordemodgui.cpp
+++ b/plugins/channelrx/demodvor/vordemodgui.cpp
@@ -1345,14 +1345,16 @@ void VORDemodGUI::displaySettings()
     blockApplySettings(false);
 }
 
-void VORDemodGUI::leaveEvent(QEvent*)
+void VORDemodGUI::leaveEvent(QEvent* event)
 {
     m_channelMarker.setHighlighted(false);
+    ChannelGUI::leaveEvent(event);
 }
 
-void VORDemodGUI::enterEvent(QEvent*)
+void VORDemodGUI::enterEvent(QEvent* event)
 {
     m_channelMarker.setHighlighted(true);
+    ChannelGUI::enterEvent(event);
 }
 
 void VORDemodGUI::audioSelect()
diff --git a/plugins/channelrx/demodvorsc/vordemodscgui.cpp b/plugins/channelrx/demodvorsc/vordemodscgui.cpp
index d0e7c0e0a..2cfe7740e 100644
--- a/plugins/channelrx/demodvorsc/vordemodscgui.cpp
+++ b/plugins/channelrx/demodvorsc/vordemodscgui.cpp
@@ -388,14 +388,16 @@ void VORDemodSCGUI::displaySettings()
     blockApplySettings(false);
 }
 
-void VORDemodSCGUI::leaveEvent(QEvent*)
+void VORDemodSCGUI::leaveEvent(QEvent* event)
 {
     m_channelMarker.setHighlighted(false);
+    ChannelGUI::leaveEvent(event);
 }
 
-void VORDemodSCGUI::enterEvent(QEvent*)
+void VORDemodSCGUI::enterEvent(QEvent* event)
 {
     m_channelMarker.setHighlighted(true);
+    ChannelGUI::enterEvent(event);
 }
 
 void VORDemodSCGUI::audioSelect()
diff --git a/plugins/channelrx/demodwfm/wfmdemodgui.cpp b/plugins/channelrx/demodwfm/wfmdemodgui.cpp
index 50a5b723f..5d90fd636 100644
--- a/plugins/channelrx/demodwfm/wfmdemodgui.cpp
+++ b/plugins/channelrx/demodwfm/wfmdemodgui.cpp
@@ -315,14 +315,16 @@ void WFMDemodGUI::displaySettings()
     blockApplySettings(false);
 }
 
-void WFMDemodGUI::leaveEvent(QEvent*)
+void WFMDemodGUI::leaveEvent(QEvent* event)
 {
 	m_channelMarker.setHighlighted(false);
+    ChannelGUI::leaveEvent(event);
 }
 
-void WFMDemodGUI::enterEvent(QEvent*)
+void WFMDemodGUI::enterEvent(QEvent* event)
 {
 	m_channelMarker.setHighlighted(true);
+    ChannelGUI::enterEvent(event);
 }
 
 void WFMDemodGUI::audioSelect()
diff --git a/plugins/channelrx/filesink/filesinkgui.cpp b/plugins/channelrx/filesink/filesinkgui.cpp
index 04f51aa65..e2ab550d1 100644
--- a/plugins/channelrx/filesink/filesinkgui.cpp
+++ b/plugins/channelrx/filesink/filesinkgui.cpp
@@ -305,14 +305,16 @@ void FileSinkGUI::displayPos()
     ui->filterChainIndex->setText(tr("%1").arg(m_fixedShiftIndex));
 }
 
-void FileSinkGUI::leaveEvent(QEvent*)
+void FileSinkGUI::leaveEvent(QEvent* event)
 {
     m_channelMarker.setHighlighted(false);
+    ChannelGUI::leaveEvent(event);
 }
 
-void FileSinkGUI::enterEvent(QEvent*)
+void FileSinkGUI::enterEvent(QEvent* event)
 {
     m_channelMarker.setHighlighted(true);
+    ChannelGUI::enterEvent(event);
 }
 
 void FileSinkGUI::channelMarkerChangedByCursor()
diff --git a/plugins/channelrx/freqtracker/freqtrackergui.cpp b/plugins/channelrx/freqtracker/freqtrackergui.cpp
index 7aa2512c6..3e946492f 100644
--- a/plugins/channelrx/freqtracker/freqtrackergui.cpp
+++ b/plugins/channelrx/freqtracker/freqtrackergui.cpp
@@ -465,14 +465,16 @@ void FreqTrackerGUI::displaySpectrumBandwidth(int spanLog2)
     ui->glSpectrum->setSampleRate(spectrumRate);
 }
 
-void FreqTrackerGUI::leaveEvent(QEvent*)
+void FreqTrackerGUI::leaveEvent(QEvent* event)
 {
 	m_channelMarker.setHighlighted(false);
+    ChannelGUI::leaveEvent(event);
 }
 
-void FreqTrackerGUI::enterEvent(QEvent*)
+void FreqTrackerGUI::enterEvent(QEvent* event)
 {
 	m_channelMarker.setHighlighted(true);
+    ChannelGUI::enterEvent(event);
 }
 
 void FreqTrackerGUI::tick()
diff --git a/plugins/channelrx/localsink/localsinkgui.cpp b/plugins/channelrx/localsink/localsinkgui.cpp
index 89d85d450..399cae251 100644
--- a/plugins/channelrx/localsink/localsinkgui.cpp
+++ b/plugins/channelrx/localsink/localsinkgui.cpp
@@ -223,14 +223,16 @@ int LocalSinkGUI::getLocalDeviceIndexInCombo(int localDeviceIndex)
     return -1;
 }
 
-void LocalSinkGUI::leaveEvent(QEvent*)
+void LocalSinkGUI::leaveEvent(QEvent* event)
 {
     m_channelMarker.setHighlighted(false);
+    ChannelGUI::leaveEvent(event);
 }
 
-void LocalSinkGUI::enterEvent(QEvent*)
+void LocalSinkGUI::enterEvent(QEvent* event)
 {
     m_channelMarker.setHighlighted(true);
+    ChannelGUI::enterEvent(event);
 }
 
 void LocalSinkGUI::handleSourceMessages()
diff --git a/plugins/channelrx/noisefigure/noisefiguregui.cpp b/plugins/channelrx/noisefigure/noisefiguregui.cpp
index c2bc9a0db..765d13515 100644
--- a/plugins/channelrx/noisefigure/noisefiguregui.cpp
+++ b/plugins/channelrx/noisefigure/noisefiguregui.cpp
@@ -758,14 +758,16 @@ void NoiseFigureGUI::displaySettings()
     blockApplySettings(false);
 }
 
-void NoiseFigureGUI::leaveEvent(QEvent*)
+void NoiseFigureGUI::leaveEvent(QEvent* event)
 {
     m_channelMarker.setHighlighted(false);
+    ChannelGUI::leaveEvent(event);
 }
 
-void NoiseFigureGUI::enterEvent(QEvent*)
+void NoiseFigureGUI::enterEvent(QEvent* event)
 {
     m_channelMarker.setHighlighted(true);
+    ChannelGUI::enterEvent(event);
 }
 
 void NoiseFigureGUI::tick()
diff --git a/plugins/channelrx/radioastronomy/radioastronomygui.cpp b/plugins/channelrx/radioastronomy/radioastronomygui.cpp
index 8a3996162..5fe80f66a 100644
--- a/plugins/channelrx/radioastronomy/radioastronomygui.cpp
+++ b/plugins/channelrx/radioastronomy/radioastronomygui.cpp
@@ -2569,14 +2569,16 @@ void RadioAstronomyGUI::displaySettings()
     getRollupContents()->arrangeRollups();
 }
 
-void RadioAstronomyGUI::leaveEvent(QEvent*)
+void RadioAstronomyGUI::leaveEvent(QEvent* event)
 {
     m_channelMarker.setHighlighted(false);
+    ChannelGUI::leaveEvent(event);
 }
 
-void RadioAstronomyGUI::enterEvent(QEvent*)
+void RadioAstronomyGUI::enterEvent(QEvent* event)
 {
     m_channelMarker.setHighlighted(true);
+    ChannelGUI::enterEvent(event);
 }
 
 void RadioAstronomyGUI::tick()
diff --git a/plugins/channelrx/radioclock/radioclockgui.cpp b/plugins/channelrx/radioclock/radioclockgui.cpp
index 675463e1e..826736eab 100644
--- a/plugins/channelrx/radioclock/radioclockgui.cpp
+++ b/plugins/channelrx/radioclock/radioclockgui.cpp
@@ -390,14 +390,16 @@ void RadioClockGUI::displaySettings()
     blockApplySettings(false);
 }
 
-void RadioClockGUI::leaveEvent(QEvent*)
+void RadioClockGUI::leaveEvent(QEvent* event)
 {
     m_channelMarker.setHighlighted(false);
+    ChannelGUI::leaveEvent(event);
 }
 
-void RadioClockGUI::enterEvent(QEvent*)
+void RadioClockGUI::enterEvent(QEvent* event)
 {
     m_channelMarker.setHighlighted(true);
+    ChannelGUI::enterEvent(event);
 }
 
 void RadioClockGUI::tick()
diff --git a/plugins/channelrx/remotesink/remotesinkgui.cpp b/plugins/channelrx/remotesink/remotesinkgui.cpp
index 50f4cf9ad..eaeae5cb2 100644
--- a/plugins/channelrx/remotesink/remotesinkgui.cpp
+++ b/plugins/channelrx/remotesink/remotesinkgui.cpp
@@ -192,14 +192,16 @@ void RemoteSinkGUI::displayRateAndShift()
     m_channelMarker.setBandwidth(channelSampleRate);
 }
 
-void RemoteSinkGUI::leaveEvent(QEvent*)
+void RemoteSinkGUI::leaveEvent(QEvent* event)
 {
     m_channelMarker.setHighlighted(false);
+    ChannelGUI::leaveEvent(event);
 }
 
-void RemoteSinkGUI::enterEvent(QEvent*)
+void RemoteSinkGUI::enterEvent(QEvent* event)
 {
     m_channelMarker.setHighlighted(true);
+    ChannelGUI::enterEvent(event);
 }
 
 void RemoteSinkGUI::handleSourceMessages()
diff --git a/plugins/channelrx/sigmffilesink/sigmffilesinkgui.cpp b/plugins/channelrx/sigmffilesink/sigmffilesinkgui.cpp
index 28606ff7a..48c10287e 100644
--- a/plugins/channelrx/sigmffilesink/sigmffilesinkgui.cpp
+++ b/plugins/channelrx/sigmffilesink/sigmffilesinkgui.cpp
@@ -297,14 +297,16 @@ void SigMFFileSinkGUI::displayPos()
     ui->filterChainIndex->setText(tr("%1").arg(m_fixedShiftIndex));
 }
 
-void SigMFFileSinkGUI::leaveEvent(QEvent*)
+void SigMFFileSinkGUI::leaveEvent(QEvent* event)
 {
     m_channelMarker.setHighlighted(false);
+    ChannelGUI::leaveEvent(event);
 }
 
-void SigMFFileSinkGUI::enterEvent(QEvent*)
+void SigMFFileSinkGUI::enterEvent(QEvent* event)
 {
     m_channelMarker.setHighlighted(true);
+    ChannelGUI::enterEvent(event);
 }
 
 void SigMFFileSinkGUI::channelMarkerChangedByCursor()
diff --git a/plugins/channelrx/udpsink/udpsinkgui.cpp b/plugins/channelrx/udpsink/udpsinkgui.cpp
index 855c7751d..8f2269cf4 100644
--- a/plugins/channelrx/udpsink/udpsinkgui.cpp
+++ b/plugins/channelrx/udpsink/udpsinkgui.cpp
@@ -649,14 +649,16 @@ void UDPSinkGUI::onMenuDialogCalled(const QPoint &p)
     resetContextMenuType();
 }
 
-void UDPSinkGUI::leaveEvent(QEvent*)
+void UDPSinkGUI::leaveEvent(QEvent* event)
 {
 	m_channelMarker.setHighlighted(false);
+    ChannelGUI::leaveEvent(event);
 }
 
-void UDPSinkGUI::enterEvent(QEvent*)
+void UDPSinkGUI::enterEvent(QEvent* event)
 {
 	m_channelMarker.setHighlighted(true);
+    ChannelGUI::enterEvent(event);
 }
 
 void UDPSinkGUI::makeUIConnections()
diff --git a/plugins/channeltx/filesource/filesourcegui.cpp b/plugins/channeltx/filesource/filesourcegui.cpp
index f0e0bdda7..6bff969f8 100644
--- a/plugins/channeltx/filesource/filesourcegui.cpp
+++ b/plugins/channeltx/filesource/filesourcegui.cpp
@@ -333,14 +333,16 @@ void FileSourceGUI::displayRateAndShift()
     m_channelMarker.setBandwidth(channelSampleRate);
 }
 
-void FileSourceGUI::leaveEvent(QEvent*)
+void FileSourceGUI::leaveEvent(QEvent* event)
 {
     m_channelMarker.setHighlighted(false);
+    ChannelGUI::leaveEvent(event);
 }
 
-void FileSourceGUI::enterEvent(QEvent*)
+void FileSourceGUI::enterEvent(QEvent* event)
 {
     m_channelMarker.setHighlighted(true);
+    ChannelGUI::enterEvent(event);
 }
 
 void FileSourceGUI::handleSourceMessages()
diff --git a/plugins/channeltx/localsource/localsourcegui.cpp b/plugins/channeltx/localsource/localsourcegui.cpp
index 1a1e346c1..e18769251 100644
--- a/plugins/channeltx/localsource/localsourcegui.cpp
+++ b/plugins/channeltx/localsource/localsourcegui.cpp
@@ -198,14 +198,16 @@ void LocalSourceGUI::updateLocalDevices()
     }
 }
 
-void LocalSourceGUI::leaveEvent(QEvent*)
+void LocalSourceGUI::leaveEvent(QEvent* event)
 {
     m_channelMarker.setHighlighted(false);
+    ChannelGUI::leaveEvent(event);
 }
 
-void LocalSourceGUI::enterEvent(QEvent*)
+void LocalSourceGUI::enterEvent(QEvent* event)
 {
     m_channelMarker.setHighlighted(true);
+    ChannelGUI::enterEvent(event);
 }
 
 void LocalSourceGUI::handleSourceMessages()
diff --git a/plugins/channeltx/mod802.15.4/ieee_802_15_4_modgui.cpp b/plugins/channeltx/mod802.15.4/ieee_802_15_4_modgui.cpp
index c5cbe8e36..fa95810e0 100644
--- a/plugins/channeltx/mod802.15.4/ieee_802_15_4_modgui.cpp
+++ b/plugins/channeltx/mod802.15.4/ieee_802_15_4_modgui.cpp
@@ -605,14 +605,16 @@ QString IEEE_802_15_4_ModGUI::getDisplayValueWithMultiplier(int value)
     }
 }
 
-void IEEE_802_15_4_ModGUI::leaveEvent(QEvent*)
+void IEEE_802_15_4_ModGUI::leaveEvent(QEvent* event)
 {
     m_channelMarker.setHighlighted(false);
+    ChannelGUI::leaveEvent(event);
 }
 
-void IEEE_802_15_4_ModGUI::enterEvent(QEvent*)
+void IEEE_802_15_4_ModGUI::enterEvent(QEvent* event)
 {
     m_channelMarker.setHighlighted(true);
+    ChannelGUI::enterEvent(event);
 }
 
 void IEEE_802_15_4_ModGUI::tick()
diff --git a/plugins/channeltx/modais/aismodgui.cpp b/plugins/channeltx/modais/aismodgui.cpp
index 2348d50c1..4a6f74a47 100644
--- a/plugins/channeltx/modais/aismodgui.cpp
+++ b/plugins/channeltx/modais/aismodgui.cpp
@@ -575,14 +575,16 @@ void AISModGUI::displaySettings()
     blockApplySettings(false);
 }
 
-void AISModGUI::leaveEvent(QEvent*)
+void AISModGUI::leaveEvent(QEvent* event)
 {
     m_channelMarker.setHighlighted(false);
+    ChannelGUI::leaveEvent(event);
 }
 
-void AISModGUI::enterEvent(QEvent*)
+void AISModGUI::enterEvent(QEvent* event)
 {
     m_channelMarker.setHighlighted(true);
+    ChannelGUI::enterEvent(event);
 }
 
 void AISModGUI::tick()
diff --git a/plugins/channeltx/modam/ammodgui.cpp b/plugins/channeltx/modam/ammodgui.cpp
index ba71b516c..e7b71bfb4 100644
--- a/plugins/channeltx/modam/ammodgui.cpp
+++ b/plugins/channeltx/modam/ammodgui.cpp
@@ -480,14 +480,16 @@ void AMModGUI::displaySettings()
     blockApplySettings(false);
 }
 
-void AMModGUI::leaveEvent(QEvent*)
+void AMModGUI::leaveEvent(QEvent* event)
 {
 	m_channelMarker.setHighlighted(false);
+    ChannelGUI::leaveEvent(event);
 }
 
-void AMModGUI::enterEvent(QEvent*)
+void AMModGUI::enterEvent(QEvent* event)
 {
 	m_channelMarker.setHighlighted(true);
+    ChannelGUI::enterEvent(event);
 }
 
 void AMModGUI::audioSelect()
diff --git a/plugins/channeltx/modatv/atvmodgui.cpp b/plugins/channeltx/modatv/atvmodgui.cpp
index c3dc7d76e..511765c84 100644
--- a/plugins/channeltx/modatv/atvmodgui.cpp
+++ b/plugins/channeltx/modatv/atvmodgui.cpp
@@ -818,14 +818,16 @@ void ATVModGUI::displaySettings()
     blockApplySettings(false);
 }
 
-void ATVModGUI::leaveEvent(QEvent*)
+void ATVModGUI::leaveEvent(QEvent* event)
 {
 	m_channelMarker.setHighlighted(false);
+    ChannelGUI::leaveEvent(event);
 }
 
-void ATVModGUI::enterEvent(QEvent*)
+void ATVModGUI::enterEvent(QEvent* event)
 {
 	m_channelMarker.setHighlighted(true);
+    ChannelGUI::enterEvent(event);
 }
 
 void ATVModGUI::tick()
diff --git a/plugins/channeltx/modchirpchat/chirpchatmodgui.cpp b/plugins/channeltx/modchirpchat/chirpchatmodgui.cpp
index b8af4b4ec..c30310e72 100644
--- a/plugins/channeltx/modchirpchat/chirpchatmodgui.cpp
+++ b/plugins/channeltx/modchirpchat/chirpchatmodgui.cpp
@@ -592,14 +592,16 @@ void ChirpChatModGUI::setBandwidths()
     }
 }
 
-void ChirpChatModGUI::leaveEvent(QEvent*)
+void ChirpChatModGUI::leaveEvent(QEvent* event)
 {
 	m_channelMarker.setHighlighted(false);
+    ChannelGUI::leaveEvent(event);
 }
 
-void ChirpChatModGUI::enterEvent(QEvent*)
+void ChirpChatModGUI::enterEvent(QEvent* event)
 {
 	m_channelMarker.setHighlighted(true);
+    ChannelGUI::enterEvent(event);
 }
 
 void ChirpChatModGUI::tick()
diff --git a/plugins/channeltx/moddatv/datvmodgui.cpp b/plugins/channeltx/moddatv/datvmodgui.cpp
index 370c2945d..5040dd02a 100644
--- a/plugins/channeltx/moddatv/datvmodgui.cpp
+++ b/plugins/channeltx/moddatv/datvmodgui.cpp
@@ -604,14 +604,16 @@ void DATVModGUI::displaySettings()
     blockApplySettings(false);
 }
 
-void DATVModGUI::leaveEvent(QEvent*)
+void DATVModGUI::leaveEvent(QEvent* event)
 {
     m_channelMarker.setHighlighted(false);
+    ChannelGUI::leaveEvent(event);
 }
 
-void DATVModGUI::enterEvent(QEvent*)
+void DATVModGUI::enterEvent(QEvent* event)
 {
     m_channelMarker.setHighlighted(true);
+    ChannelGUI::enterEvent(event);
 }
 
 void DATVModGUI::tick()
diff --git a/plugins/channeltx/modfreedv/freedvmodgui.cpp b/plugins/channeltx/modfreedv/freedvmodgui.cpp
index 406f65e2e..6adc7b4fe 100644
--- a/plugins/channeltx/modfreedv/freedvmodgui.cpp
+++ b/plugins/channeltx/modfreedv/freedvmodgui.cpp
@@ -522,14 +522,16 @@ void FreeDVModGUI::displaySettings()
     blockApplySettings(false);
 }
 
-void FreeDVModGUI::leaveEvent(QEvent*)
+void FreeDVModGUI::leaveEvent(QEvent* event)
 {
 	m_channelMarker.setHighlighted(false);
+    ChannelGUI::leaveEvent(event);
 }
 
-void FreeDVModGUI::enterEvent(QEvent*)
+void FreeDVModGUI::enterEvent(QEvent* event)
 {
 	m_channelMarker.setHighlighted(true);
+    ChannelGUI::enterEvent(event);
 }
 
 void FreeDVModGUI::audioSelect()
diff --git a/plugins/channeltx/modnfm/nfmmodgui.cpp b/plugins/channeltx/modnfm/nfmmodgui.cpp
index e1023b923..c5221d99d 100644
--- a/plugins/channeltx/modnfm/nfmmodgui.cpp
+++ b/plugins/channeltx/modnfm/nfmmodgui.cpp
@@ -579,14 +579,16 @@ void NFMModGUI::displaySettings()
     blockApplySettings(false);
 }
 
-void NFMModGUI::leaveEvent(QEvent*)
+void NFMModGUI::leaveEvent(QEvent* event)
 {
 	m_channelMarker.setHighlighted(false);
+    ChannelGUI::leaveEvent(event);
 }
 
-void NFMModGUI::enterEvent(QEvent*)
+void NFMModGUI::enterEvent(QEvent* event)
 {
 	m_channelMarker.setHighlighted(true);
+    ChannelGUI::enterEvent(event);
 }
 
 void NFMModGUI::audioSelect()
diff --git a/plugins/channeltx/modpacket/packetmodgui.cpp b/plugins/channeltx/modpacket/packetmodgui.cpp
index a1bd5ac8e..621314241 100644
--- a/plugins/channeltx/modpacket/packetmodgui.cpp
+++ b/plugins/channeltx/modpacket/packetmodgui.cpp
@@ -595,14 +595,16 @@ void PacketModGUI::displaySettings()
     blockApplySettings(false);
 }
 
-void PacketModGUI::leaveEvent(QEvent*)
+void PacketModGUI::leaveEvent(QEvent* event)
 {
     m_channelMarker.setHighlighted(false);
+    ChannelGUI::leaveEvent(event);
 }
 
-void PacketModGUI::enterEvent(QEvent*)
+void PacketModGUI::enterEvent(QEvent* event)
 {
     m_channelMarker.setHighlighted(true);
+    ChannelGUI::enterEvent(event);
 }
 
 void PacketModGUI::tick()
diff --git a/plugins/channeltx/modssb/ssbmodgui.cpp b/plugins/channeltx/modssb/ssbmodgui.cpp
index b55f10b46..c9a8696f3 100644
--- a/plugins/channeltx/modssb/ssbmodgui.cpp
+++ b/plugins/channeltx/modssb/ssbmodgui.cpp
@@ -736,14 +736,16 @@ void SSBModGUI::displaySettings()
     blockApplySettings(false);
 }
 
-void SSBModGUI::leaveEvent(QEvent*)
+void SSBModGUI::leaveEvent(QEvent* event)
 {
 	m_channelMarker.setHighlighted(false);
+    ChannelGUI::leaveEvent(event);
 }
 
-void SSBModGUI::enterEvent(QEvent*)
+void SSBModGUI::enterEvent(QEvent* event)
 {
 	m_channelMarker.setHighlighted(true);
+    ChannelGUI::enterEvent(event);
 }
 
 void SSBModGUI::audioSelect()
diff --git a/plugins/channeltx/modwfm/wfmmodgui.cpp b/plugins/channeltx/modwfm/wfmmodgui.cpp
index 1da374cfd..d9d744c22 100644
--- a/plugins/channeltx/modwfm/wfmmodgui.cpp
+++ b/plugins/channeltx/modwfm/wfmmodgui.cpp
@@ -497,14 +497,16 @@ void WFMModGUI::displaySettings()
     blockApplySettings(false);
 }
 
-void WFMModGUI::leaveEvent(QEvent*)
+void WFMModGUI::leaveEvent(QEvent* event)
 {
 	m_channelMarker.setHighlighted(false);
+    ChannelGUI::leaveEvent(event);
 }
 
-void WFMModGUI::enterEvent(QEvent*)
+void WFMModGUI::enterEvent(QEvent* event)
 {
 	m_channelMarker.setHighlighted(true);
+    ChannelGUI::enterEvent(event);
 }
 
 void WFMModGUI::audioSelect()
diff --git a/plugins/channeltx/remotesource/remotesourcegui.cpp b/plugins/channeltx/remotesource/remotesourcegui.cpp
index 7bf6f5198..5ca564926 100644
--- a/plugins/channeltx/remotesource/remotesourcegui.cpp
+++ b/plugins/channeltx/remotesource/remotesourcegui.cpp
@@ -271,14 +271,16 @@ void RemoteSourceGUI::displayPosition()
     ui->filterChainText->setText(s);
 }
 
-void RemoteSourceGUI::leaveEvent(QEvent*)
+void RemoteSourceGUI::leaveEvent(QEvent* event)
 {
     m_channelMarker.setHighlighted(false);
+    ChannelGUI::leaveEvent(event);
 }
 
-void RemoteSourceGUI::enterEvent(QEvent*)
+void RemoteSourceGUI::enterEvent(QEvent* event)
 {
     m_channelMarker.setHighlighted(true);
+    ChannelGUI::enterEvent(event);
 }
 
 void RemoteSourceGUI::handleSourceMessages()
diff --git a/plugins/channeltx/udpsource/udpsourcegui.cpp b/plugins/channeltx/udpsource/udpsourcegui.cpp
index da0f41275..2e48c1598 100644
--- a/plugins/channeltx/udpsource/udpsourcegui.cpp
+++ b/plugins/channeltx/udpsource/udpsourcegui.cpp
@@ -530,14 +530,16 @@ void UDPSourceGUI::onMenuDialogCalled(const QPoint &p)
     resetContextMenuType();
 }
 
-void UDPSourceGUI::leaveEvent(QEvent*)
+void UDPSourceGUI::leaveEvent(QEvent* event)
 {
     m_channelMarker.setHighlighted(false);
+    ChannelGUI::leaveEvent(event);
 }
 
-void UDPSourceGUI::enterEvent(QEvent*)
+void UDPSourceGUI::enterEvent(QEvent* event)
 {
     m_channelMarker.setHighlighted(true);
+    ChannelGUI::enterEvent(event);
 }
 
 void UDPSourceGUI::tick()
diff --git a/plugins/feature/afc/afcgui.cpp b/plugins/feature/afc/afcgui.cpp
index a5924e9ea..ebf176d26 100644
--- a/plugins/feature/afc/afcgui.cpp
+++ b/plugins/feature/afc/afcgui.cpp
@@ -255,14 +255,6 @@ void AFCGUI::updateDeviceSetLists(const AFC::MsgDeviceSetListsReport& report)
     ui->trackedDevice->blockSignals(false);
 }
 
-void AFCGUI::leaveEvent(QEvent*)
-{
-}
-
-void AFCGUI::enterEvent(QEvent*)
-{
-}
-
 void AFCGUI::onMenuDialogCalled(const QPoint &p)
 {
     if (m_contextMenuType == ContextMenuChannelSettings)
diff --git a/plugins/feature/afc/afcgui.h b/plugins/feature/afc/afcgui.h
index 83c73a537..a29e49503 100644
--- a/plugins/feature/afc/afcgui.h
+++ b/plugins/feature/afc/afcgui.h
@@ -73,9 +73,6 @@ private:
 	bool handleMessage(const Message& message);
     void makeUIConnections();
 
-	void leaveEvent(QEvent*);
-	void enterEvent(QEvent*);
-
 private slots:
 	void onMenuDialogCalled(const QPoint &p);
 	void onWidgetRolled(QWidget* widget, bool rollDown);
diff --git a/plugins/feature/ais/aisgui.cpp b/plugins/feature/ais/aisgui.cpp
index 79a6656cc..4ea066702 100644
--- a/plugins/feature/ais/aisgui.cpp
+++ b/plugins/feature/ais/aisgui.cpp
@@ -257,14 +257,6 @@ void AISGUI::displaySettings()
     getRollupContents()->arrangeRollups();
 }
 
-void AISGUI::leaveEvent(QEvent*)
-{
-}
-
-void AISGUI::enterEvent(QEvent*)
-{
-}
-
 void AISGUI::onMenuDialogCalled(const QPoint &p)
 {
     if (m_contextMenuType == ContextMenuChannelSettings)
diff --git a/plugins/feature/ais/aisgui.h b/plugins/feature/ais/aisgui.h
index 7bc3651a6..107276ffb 100644
--- a/plugins/feature/ais/aisgui.h
+++ b/plugins/feature/ais/aisgui.h
@@ -92,9 +92,6 @@ private:
     void displaySettings();
     bool handleMessage(const Message& message);
 
-    void leaveEvent(QEvent*);
-    void enterEvent(QEvent*);
-
     void sendToMap(const QString &name, const QString &label,
         const QString &image, const QString &text,
         const QString &model, float modelOffset, float labelOffset,
diff --git a/plugins/feature/antennatools/antennatoolsgui.cpp b/plugins/feature/antennatools/antennatoolsgui.cpp
index b1e55847f..d3051f407 100644
--- a/plugins/feature/antennatools/antennatoolsgui.cpp
+++ b/plugins/feature/antennatools/antennatoolsgui.cpp
@@ -190,14 +190,6 @@ void AntennaToolsGUI::makeUIConnections()
     QObject::connect(ui->dishSurfaceError, qOverload<double>(&QDoubleSpinBox::valueChanged), this, &AntennaToolsGUI::on_dishSurfaceError_valueChanged);
 }
 
-void AntennaToolsGUI::leaveEvent(QEvent*)
-{
-}
-
-void AntennaToolsGUI::enterEvent(QEvent*)
-{
-}
-
 void AntennaToolsGUI::onMenuDialogCalled(const QPoint &p)
 {
     if (m_contextMenuType == ContextMenuChannelSettings)
diff --git a/plugins/feature/antennatools/antennatoolsgui.h b/plugins/feature/antennatools/antennatoolsgui.h
index 95823f9a3..ad4b74ceb 100644
--- a/plugins/feature/antennatools/antennatoolsgui.h
+++ b/plugins/feature/antennatools/antennatoolsgui.h
@@ -73,9 +73,6 @@ private:
     bool handleMessage(const Message& message);
     void makeUIConnections();
 
-    void leaveEvent(QEvent*);
-    void enterEvent(QEvent*);
-
     void calcDipoleLength();
     double calcDipoleFrequency(double totalLength);
     void calcDishFocalLength();
diff --git a/plugins/feature/aprs/aprsgui.cpp b/plugins/feature/aprs/aprsgui.cpp
index f83e50f60..80ed4108c 100644
--- a/plugins/feature/aprs/aprsgui.cpp
+++ b/plugins/feature/aprs/aprsgui.cpp
@@ -660,14 +660,6 @@ void APRSGUI::updateChannelList()
     ui->sourcePipes->blockSignals(false);
 }
 
-void APRSGUI::leaveEvent(QEvent*)
-{
-}
-
-void APRSGUI::enterEvent(QEvent*)
-{
-}
-
 void APRSGUI::resizeEvent(QResizeEvent* size)
 {
     // Replot graphs to ensure Axis are visible
diff --git a/plugins/feature/aprs/aprsgui.h b/plugins/feature/aprs/aprsgui.h
index f1a89d1e5..4b9c67442 100644
--- a/plugins/feature/aprs/aprsgui.h
+++ b/plugins/feature/aprs/aprsgui.h
@@ -161,9 +161,6 @@ private:
     bool handleMessage(const Message& message);
     void makeUIConnections();
 
-    void leaveEvent(QEvent*);
-    void enterEvent(QEvent*);
-
     void filterMessageRow(int row);
     void filterMessages();
     void resizeTable();
diff --git a/plugins/feature/demodanalyzer/demodanalyzergui.cpp b/plugins/feature/demodanalyzer/demodanalyzergui.cpp
index 0fefd371e..9872a0bfb 100644
--- a/plugins/feature/demodanalyzer/demodanalyzergui.cpp
+++ b/plugins/feature/demodanalyzer/demodanalyzergui.cpp
@@ -239,14 +239,6 @@ void DemodAnalyzerGUI::updateChannelList()
     }
 }
 
-void DemodAnalyzerGUI::leaveEvent(QEvent*)
-{
-}
-
-void DemodAnalyzerGUI::enterEvent(QEvent*)
-{
-}
-
 void DemodAnalyzerGUI::onMenuDialogCalled(const QPoint &p)
 {
     if (m_contextMenuType == ContextMenuChannelSettings)
diff --git a/plugins/feature/demodanalyzer/demodanalyzergui.h b/plugins/feature/demodanalyzer/demodanalyzergui.h
index e26fddd60..d21c0b1c3 100644
--- a/plugins/feature/demodanalyzer/demodanalyzergui.h
+++ b/plugins/feature/demodanalyzer/demodanalyzergui.h
@@ -84,9 +84,6 @@ private:
 	bool handleMessage(const Message& message);
     void makeUIConnections();
 
-	void leaveEvent(QEvent*);
-	void enterEvent(QEvent*);
-
 private slots:
 	void onMenuDialogCalled(const QPoint &p);
 	void onWidgetRolled(QWidget* widget, bool rollDown);
diff --git a/plugins/feature/gs232controller/gs232controllergui.cpp b/plugins/feature/gs232controller/gs232controllergui.cpp
index e777ee052..d90da437e 100644
--- a/plugins/feature/gs232controller/gs232controllergui.cpp
+++ b/plugins/feature/gs232controller/gs232controllergui.cpp
@@ -284,14 +284,6 @@ void GS232ControllerGUI::updatePipeList(const QList<GS232ControllerSettings::Ava
     // }
 }
 
-void GS232ControllerGUI::leaveEvent(QEvent*)
-{
-}
-
-void GS232ControllerGUI::enterEvent(QEvent*)
-{
-}
-
 void GS232ControllerGUI::onMenuDialogCalled(const QPoint &p)
 {
     if (m_contextMenuType == ContextMenuChannelSettings)
diff --git a/plugins/feature/gs232controller/gs232controllergui.h b/plugins/feature/gs232controller/gs232controllergui.h
index 887d5d783..0a0c2c6aa 100644
--- a/plugins/feature/gs232controller/gs232controllergui.h
+++ b/plugins/feature/gs232controller/gs232controllergui.h
@@ -77,9 +77,6 @@ private:
     bool handleMessage(const Message& message);
     void makeUIConnections();
 
-    void leaveEvent(QEvent*);
-    void enterEvent(QEvent*);
-
 private slots:
     void onMenuDialogCalled(const QPoint &p);
     void onWidgetRolled(QWidget* widget, bool rollDown);
diff --git a/plugins/feature/jogdialcontroller/jogdialcontrollergui.cpp b/plugins/feature/jogdialcontroller/jogdialcontrollergui.cpp
index 3db3ca647..b8eaab4df 100644
--- a/plugins/feature/jogdialcontroller/jogdialcontrollergui.cpp
+++ b/plugins/feature/jogdialcontroller/jogdialcontrollergui.cpp
@@ -229,14 +229,6 @@ void JogdialControllerGUI::updateChannelList()
     }
 }
 
-void JogdialControllerGUI::leaveEvent(QEvent*)
-{
-}
-
-void JogdialControllerGUI::enterEvent(QEvent*)
-{
-}
-
 void JogdialControllerGUI::onMenuDialogCalled(const QPoint &p)
 {
     if (m_contextMenuType == ContextMenuChannelSettings)
diff --git a/plugins/feature/jogdialcontroller/jogdialcontrollergui.h b/plugins/feature/jogdialcontroller/jogdialcontrollergui.h
index 932b5592d..6fa9707be 100644
--- a/plugins/feature/jogdialcontroller/jogdialcontrollergui.h
+++ b/plugins/feature/jogdialcontroller/jogdialcontrollergui.h
@@ -82,9 +82,6 @@ private:
 	bool handleMessage(const Message& message);
     void makeUIConnections();
 
-	void leaveEvent(QEvent*);
-	void enterEvent(QEvent*);
-
 private slots:
 	void onMenuDialogCalled(const QPoint &p);
 	void onWidgetRolled(QWidget* widget, bool rollDown);
diff --git a/plugins/feature/map/mapgui.cpp b/plugins/feature/map/mapgui.cpp
index 41fdfa947..fb199fc80 100644
--- a/plugins/feature/map/mapgui.cpp
+++ b/plugins/feature/map/mapgui.cpp
@@ -863,14 +863,6 @@ void MapGUI::displaySettings()
     blockApplySettings(false);
 }
 
-void MapGUI::leaveEvent(QEvent*)
-{
-}
-
-void MapGUI::enterEvent(QEvent*)
-{
-}
-
 void MapGUI::onMenuDialogCalled(const QPoint &p)
 {
     if (m_contextMenuType == ContextMenuChannelSettings)
diff --git a/plugins/feature/map/mapgui.h b/plugins/feature/map/mapgui.h
index 774810f8d..2c5d7f625 100644
--- a/plugins/feature/map/mapgui.h
+++ b/plugins/feature/map/mapgui.h
@@ -138,9 +138,6 @@ private:
     void redrawMap();
     void makeUIConnections();
 
-    void leaveEvent(QEvent*);
-    void enterEvent(QEvent*);
-
     static QString getDataDir();
     static const QList<RadioTimeTransmitter> m_radioTimeTransmitters;
 
diff --git a/plugins/feature/pertester/pertestergui.cpp b/plugins/feature/pertester/pertestergui.cpp
index 55e9be8d6..e8b15309a 100644
--- a/plugins/feature/pertester/pertestergui.cpp
+++ b/plugins/feature/pertester/pertestergui.cpp
@@ -181,14 +181,6 @@ void PERTesterGUI::displaySettings()
     getRollupContents()->arrangeRollups();
 }
 
-void PERTesterGUI::leaveEvent(QEvent*)
-{
-}
-
-void PERTesterGUI::enterEvent(QEvent*)
-{
-}
-
 void PERTesterGUI::onMenuDialogCalled(const QPoint &p)
 {
     if (m_contextMenuType == ContextMenuChannelSettings)
diff --git a/plugins/feature/pertester/pertestergui.h b/plugins/feature/pertester/pertestergui.h
index 2306bf792..3f4557c1e 100644
--- a/plugins/feature/pertester/pertestergui.h
+++ b/plugins/feature/pertester/pertestergui.h
@@ -72,9 +72,6 @@ private:
     bool handleMessage(const Message& message);
     void makeUIConnections();
 
-    void leaveEvent(QEvent*);
-    void enterEvent(QEvent*);
-
 private slots:
     void onMenuDialogCalled(const QPoint &p);
     void onWidgetRolled(QWidget* widget, bool rollDown);
diff --git a/plugins/feature/radiosonde/radiosondegui.cpp b/plugins/feature/radiosonde/radiosondegui.cpp
index ba68f6b28..da49edcec 100644
--- a/plugins/feature/radiosonde/radiosondegui.cpp
+++ b/plugins/feature/radiosonde/radiosondegui.cpp
@@ -222,14 +222,6 @@ void RadiosondeGUI::displaySettings()
     getRollupContents()->arrangeRollups();
 }
 
-void RadiosondeGUI::leaveEvent(QEvent*)
-{
-}
-
-void RadiosondeGUI::enterEvent(QEvent*)
-{
-}
-
 void RadiosondeGUI::onMenuDialogCalled(const QPoint &p)
 {
     if (m_contextMenuType == ContextMenuChannelSettings)
diff --git a/plugins/feature/radiosonde/radiosondegui.h b/plugins/feature/radiosonde/radiosondegui.h
index 1bd135232..c42bd655c 100644
--- a/plugins/feature/radiosonde/radiosondegui.h
+++ b/plugins/feature/radiosonde/radiosondegui.h
@@ -104,9 +104,6 @@ private:
     bool handleMessage(const Message& message);
     void makeUIConnections();
 
-    void leaveEvent(QEvent*);
-    void enterEvent(QEvent*);
-
     void sendToMap(const QString &name, const QString &label,
         const QString &image, const QString &text,
         const QString &model, float labelOffset,
diff --git a/plugins/feature/rigctlserver/rigctlservergui.cpp b/plugins/feature/rigctlserver/rigctlservergui.cpp
index 352a36076..aa3798eba 100644
--- a/plugins/feature/rigctlserver/rigctlservergui.cpp
+++ b/plugins/feature/rigctlserver/rigctlservergui.cpp
@@ -268,14 +268,6 @@ bool RigCtlServerGUI::updateChannelList()
     return false;
 }
 
-void RigCtlServerGUI::leaveEvent(QEvent*)
-{
-}
-
-void RigCtlServerGUI::enterEvent(QEvent*)
-{
-}
-
 void RigCtlServerGUI::onMenuDialogCalled(const QPoint &p)
 {
     if (m_contextMenuType == ContextMenuChannelSettings)
diff --git a/plugins/feature/rigctlserver/rigctlservergui.h b/plugins/feature/rigctlserver/rigctlservergui.h
index 51f429a09..be69dcb12 100644
--- a/plugins/feature/rigctlserver/rigctlservergui.h
+++ b/plugins/feature/rigctlserver/rigctlservergui.h
@@ -74,9 +74,6 @@ private:
 	bool handleMessage(const Message& message);
     void makeUIConnections();
 
-	void leaveEvent(QEvent*);
-	void enterEvent(QEvent*);
-
 private slots:
 	void onMenuDialogCalled(const QPoint &p);
 	void onWidgetRolled(QWidget* widget, bool rollDown);
diff --git a/plugins/feature/satellitetracker/satellitetrackergui.cpp b/plugins/feature/satellitetracker/satellitetrackergui.cpp
index fa0c4418f..6dd97e413 100644
--- a/plugins/feature/satellitetracker/satellitetrackergui.cpp
+++ b/plugins/feature/satellitetracker/satellitetrackergui.cpp
@@ -343,14 +343,6 @@ void SatelliteTrackerGUI::displaySettings()
     blockApplySettings(false);
 }
 
-void SatelliteTrackerGUI::leaveEvent(QEvent*)
-{
-}
-
-void SatelliteTrackerGUI::enterEvent(QEvent*)
-{
-}
-
 void SatelliteTrackerGUI::onMenuDialogCalled(const QPoint &p)
 {
     if (m_contextMenuType == ContextMenuChannelSettings)
diff --git a/plugins/feature/satellitetracker/satellitetrackergui.h b/plugins/feature/satellitetracker/satellitetrackergui.h
index 143d81bd6..301333216 100644
--- a/plugins/feature/satellitetracker/satellitetrackergui.h
+++ b/plugins/feature/satellitetracker/satellitetrackergui.h
@@ -136,9 +136,6 @@ private:
     void updateMapList();
     void makeUIConnections();
 
-    void leaveEvent(QEvent*);
-    void enterEvent(QEvent*);
-
 private slots:
     void onMenuDialogCalled(const QPoint &p);
     void onWidgetRolled(QWidget* widget, bool rollDown);
diff --git a/plugins/feature/simpleptt/simplepttgui.cpp b/plugins/feature/simpleptt/simplepttgui.cpp
index 163094290..c7027f1a5 100644
--- a/plugins/feature/simpleptt/simplepttgui.cpp
+++ b/plugins/feature/simpleptt/simplepttgui.cpp
@@ -288,14 +288,6 @@ void SimplePTTGUI::updateDeviceSetLists()
     ui->txDevice->blockSignals(false);
 }
 
-void SimplePTTGUI::leaveEvent(QEvent*)
-{
-}
-
-void SimplePTTGUI::enterEvent(QEvent*)
-{
-}
-
 void SimplePTTGUI::onMenuDialogCalled(const QPoint &p)
 {
     if (m_contextMenuType == ContextMenuChannelSettings)
diff --git a/plugins/feature/simpleptt/simplepttgui.h b/plugins/feature/simpleptt/simplepttgui.h
index bee5244d8..284cbeebc 100644
--- a/plugins/feature/simpleptt/simplepttgui.h
+++ b/plugins/feature/simpleptt/simplepttgui.h
@@ -75,9 +75,6 @@ private:
 	bool handleMessage(const Message& message);
     void makeUIConnections();
 
-	void leaveEvent(QEvent*);
-	void enterEvent(QEvent*);
-
 private slots:
 	void onMenuDialogCalled(const QPoint &p);
 	void onWidgetRolled(QWidget* widget, bool rollDown);
diff --git a/plugins/feature/startracker/startrackergui.cpp b/plugins/feature/startracker/startrackergui.cpp
index 33db89876..8ff5f40fe 100644
--- a/plugins/feature/startracker/startrackergui.cpp
+++ b/plugins/feature/startracker/startrackergui.cpp
@@ -451,14 +451,6 @@ void StarTrackerGUI::displaySettings()
     blockApplySettings(false);
 }
 
-void StarTrackerGUI::leaveEvent(QEvent*)
-{
-}
-
-void StarTrackerGUI::enterEvent(QEvent*)
-{
-}
-
 void StarTrackerGUI::onMenuDialogCalled(const QPoint &p)
 {
     if (m_contextMenuType == ContextMenuChannelSettings)
diff --git a/plugins/feature/startracker/startrackergui.h b/plugins/feature/startracker/startrackergui.h
index 802cfae69..689b00b39 100644
--- a/plugins/feature/startracker/startrackergui.h
+++ b/plugins/feature/startracker/startrackergui.h
@@ -159,9 +159,6 @@ private:
     void updateSolarFlux(bool all);
     void makeUIConnections();
 
-    void leaveEvent(QEvent*);
-    void enterEvent(QEvent*);
-
 private slots:
     void onMenuDialogCalled(const QPoint &p);
     void onWidgetRolled(QWidget* widget, bool rollDown);
diff --git a/plugins/feature/vorlocalizer/vorlocalizergui.cpp b/plugins/feature/vorlocalizer/vorlocalizergui.cpp
index 63648aa2e..dd5c5b0f2 100644
--- a/plugins/feature/vorlocalizer/vorlocalizergui.cpp
+++ b/plugins/feature/vorlocalizer/vorlocalizergui.cpp
@@ -1383,14 +1383,6 @@ void VORLocalizerGUI::displaySettings()
     blockApplySettings(false);
 }
 
-void VORLocalizerGUI::leaveEvent(QEvent*)
-{
-}
-
-void VORLocalizerGUI::enterEvent(QEvent*)
-{
-}
-
 void VORLocalizerGUI::updateStatus()
 {
     int state = m_vorLocalizer->getState();
diff --git a/plugins/feature/vorlocalizer/vorlocalizergui.h b/plugins/feature/vorlocalizer/vorlocalizergui.h
index 0f498a930..0f741c952 100644
--- a/plugins/feature/vorlocalizer/vorlocalizergui.h
+++ b/plugins/feature/vorlocalizer/vorlocalizergui.h
@@ -258,9 +258,6 @@ private:
     bool handleMessage(const Message& message);
     void makeUIConnections();
 
-    void leaveEvent(QEvent*);
-    void enterEvent(QEvent*);
-
     void resizeTable();
     QAction *createCheckableItem(QString& text, int idx, bool checked);
 
diff --git a/sdrgui/CMakeLists.txt b/sdrgui/CMakeLists.txt
index d83e53ec2..086def148 100644
--- a/sdrgui/CMakeLists.txt
+++ b/sdrgui/CMakeLists.txt
@@ -41,6 +41,7 @@ set(sdrgui_SOURCES
     gui/featurepresetsdialog.cpp
     gui/fftwisdomdialog.cpp
     gui/flowlayout.cpp
+    gui/framelesswindowresizer.cpp
     gui/glscope.cpp
     gui/glscopegui.cpp
     gui/glshadercolors.cpp
@@ -140,6 +141,7 @@ set(sdrgui_HEADERS
     gui/featurepresetsdialog.h
     gui/fftwisdomdialog.h
     gui/flowlayout.h
+    gui/framelesswindowresizer.h
     gui/glscope.h
     gui/glscopegui.h
     gui/glshadercolors.h
diff --git a/sdrgui/channel/channelgui.cpp b/sdrgui/channel/channelgui.cpp
index 63297c098..6a76d5066 100644
--- a/sdrgui/channel/channelgui.cpp
+++ b/sdrgui/channel/channelgui.cpp
@@ -39,7 +39,8 @@ ChannelGUI::ChannelGUI(QWidget *parent) :
     m_deviceSetIndex(0),
     m_channelIndex(0),
     m_contextMenuType(ContextMenuNone),
-    m_drag(false)
+    m_drag(false),
+    m_resizer(this)
 {
     qDebug("ChannelGUI::ChannelGUI");
     setWindowFlags(windowFlags() | Qt::FramelessWindowHint);
@@ -123,7 +124,7 @@ ChannelGUI::ChannelGUI(QWidget *parent) :
     m_statusLabel->setToolTip("Channel status");
 
     m_layouts = new QVBoxLayout();
-    m_layouts->setContentsMargins(0, 4, 0, 4);
+    m_layouts->setContentsMargins(m_resizer.m_gripSize, 4, m_resizer.m_gripSize, 4);
     m_layouts->setSpacing(2);
 
     m_topLayout = new QHBoxLayout();
@@ -137,10 +138,6 @@ ChannelGUI::ChannelGUI(QWidget *parent) :
     m_topLayout->addWidget(m_shrinkButton);
     m_topLayout->addWidget(m_hideButton);
     m_topLayout->addWidget(m_closeButton);
-    m_sizeGripTopRight = new QSizeGrip(this);
-    m_sizeGripTopRight->setStyleSheet("QSizeGrip { background-color: rgb(128, 128, 128); width: 10px; height: 10px; }");
-    m_sizeGripTopRight->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
-    m_topLayout->addWidget(m_sizeGripTopRight, 0, Qt::AlignTop | Qt::AlignRight);
 
     m_centerLayout = new QHBoxLayout();
     m_rollupContents = new RollupContents(); // Do not delete! Done in child's destructor with "delete ui"
@@ -153,7 +150,6 @@ ChannelGUI::ChannelGUI(QWidget *parent) :
     m_bottomLayout->addWidget(m_statusFrequency);
     m_bottomLayout->addWidget(m_statusLabel);
     m_sizeGripBottomRight = new QSizeGrip(this);
-    m_sizeGripBottomRight->setStyleSheet("QSizeGrip { background-color: rgb(128, 128, 128); width: 10px; height: 10px; }");
     m_sizeGripBottomRight->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
     // m_bottomLayout->addStretch(1);
     m_bottomLayout->addWidget(m_sizeGripBottomRight, 0, Qt::AlignBottom | Qt::AlignRight);
@@ -189,7 +185,6 @@ ChannelGUI::~ChannelGUI()
     delete m_sizeGripBottomRight;
     delete m_bottomLayout;
     delete m_centerLayout;
-    delete m_sizeGripTopRight;
     delete m_topLayout;
     delete m_layouts;
     delete m_statusLabel;
@@ -222,6 +217,15 @@ void ChannelGUI::mousePressEvent(QMouseEvent* event)
         m_DragPosition = event->globalPos() - pos();
         event->accept();
     }
+    else
+    {
+        m_resizer.mousePressEvent(event);
+    }
+}
+
+void ChannelGUI::mouseReleaseEvent(QMouseEvent* event)
+{
+    m_resizer.mouseReleaseEvent(event);
 }
 
 void ChannelGUI::mouseMoveEvent(QMouseEvent* event)
@@ -231,6 +235,16 @@ void ChannelGUI::mouseMoveEvent(QMouseEvent* event)
         move(event->globalPos() - m_DragPosition);
         event->accept();
     }
+    else
+    {
+        m_resizer.mouseMoveEvent(event);
+    }
+}
+
+void ChannelGUI::leaveEvent(QEvent* event)
+{
+    m_resizer.leaveEvent(event);
+    QMdiSubWindow::leaveEvent(event);
 }
 
 void ChannelGUI::activateSettingsDialog()
diff --git a/sdrgui/channel/channelgui.h b/sdrgui/channel/channelgui.h
index a933ac339..889638adf 100644
--- a/sdrgui/channel/channelgui.h
+++ b/sdrgui/channel/channelgui.h
@@ -21,6 +21,7 @@
 #include <QMdiSubWindow>
 #include <QMap>
 
+#include "gui/framelesswindowresizer.h"
 #include "export.h"
 
 class QCloseEvent;
@@ -87,9 +88,11 @@ public:
     void setStatusText(const QString& text);
 
 protected:
-    void closeEvent(QCloseEvent *event);
-    void mousePressEvent(QMouseEvent* event);
-    void mouseMoveEvent(QMouseEvent* event);
+    void closeEvent(QCloseEvent *event) override;
+    void leaveEvent(QEvent *event) override;
+    void mousePressEvent(QMouseEvent* event) override;
+    void mouseReleaseEvent(QMouseEvent* event) override;
+    void mouseMoveEvent(QMouseEvent* event) override;
     void resetContextMenuType() { m_contextMenuType = ContextMenuNone; }
     void updateIndexLabel();
 
@@ -125,11 +128,11 @@ private:
     QHBoxLayout *m_topLayout;
     QHBoxLayout *m_centerLayout;
     QHBoxLayout *m_bottomLayout;
-    QSizeGrip *m_sizeGripTopRight;
     QSizeGrip *m_sizeGripBottomRight;
     bool m_drag;
     QPoint m_DragPosition;
     QMap<QWidget*, int> m_heightsMap;
+    FramelessWindowResizer m_resizer;
 
 private slots:
     void activateSettingsDialog();
diff --git a/sdrgui/device/devicegui.cpp b/sdrgui/device/devicegui.cpp
index 6bcb2c671..b0a24260f 100644
--- a/sdrgui/device/devicegui.cpp
+++ b/sdrgui/device/devicegui.cpp
@@ -37,7 +37,8 @@ DeviceGUI::DeviceGUI(QWidget *parent) :
     m_deviceSetIndex(0),
     m_contextMenuType(ContextMenuNone),
     m_drag(false),
-    m_currentDeviceIndex(-1)
+    m_currentDeviceIndex(-1),
+    m_resizer(this)
 {
     qDebug("DeviceGUI::DeviceGUI: %p", parent);
     setWindowFlags(windowFlags() | Qt::FramelessWindowHint);
@@ -128,7 +129,7 @@ DeviceGUI::DeviceGUI(QWidget *parent) :
     m_showAllChannelsButton->setToolTip("Show all channels");
 
     m_layouts = new QVBoxLayout();
-    m_layouts->setContentsMargins(0, 4, 0, 4);
+    m_layouts->setContentsMargins(m_resizer.m_gripSize, 4, m_resizer.m_gripSize, 4);
     m_layouts->setSpacing(2);
 
     m_topLayout = new QHBoxLayout();
@@ -145,10 +146,6 @@ DeviceGUI::DeviceGUI(QWidget *parent) :
     m_topLayout->addWidget(m_moveButton);
     m_topLayout->addWidget(m_shrinkButton);
     m_topLayout->addWidget(m_closeButton);
-    m_sizeGripTopRight = new QSizeGrip(this);
-    m_sizeGripTopRight->setStyleSheet("QSizeGrip { background-color: rgb(128, 128, 128); width: 10px; height: 10px; }");
-    m_sizeGripTopRight->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
-    m_topLayout->addWidget(m_sizeGripTopRight, 0, Qt::AlignTop | Qt::AlignRight);
 
     m_centerLayout = new QHBoxLayout();
     m_contents = new QWidget(); // Do not delete! Done in child's destructor with "delete ui"
@@ -160,7 +157,6 @@ DeviceGUI::DeviceGUI(QWidget *parent) :
     m_bottomLayout->addWidget(m_showAllChannelsButton);
     m_bottomLayout->addWidget(m_statusLabel);
     m_sizeGripBottomRight = new QSizeGrip(this);
-    m_sizeGripBottomRight->setStyleSheet("QSizeGrip { background-color: rgb(128, 128, 128); width: 10px; height: 10px; }");
     m_sizeGripBottomRight->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
     // m_bottomLayout->addStretch(1);
     m_bottomLayout->addWidget(m_sizeGripBottomRight, 0, Qt::AlignBottom | Qt::AlignRight);
@@ -199,7 +195,6 @@ DeviceGUI::~DeviceGUI()
     delete m_sizeGripBottomRight;
     delete m_bottomLayout;
     delete m_centerLayout;
-    delete m_sizeGripTopRight;
     delete m_topLayout;
     delete m_layouts;
     delete m_showAllChannelsButton;
@@ -234,6 +229,15 @@ void DeviceGUI::mousePressEvent(QMouseEvent* event)
         m_DragPosition = event->globalPos() - pos();
         event->accept();
     }
+    else
+    {
+        m_resizer.mousePressEvent(event);
+    }
+}
+
+void DeviceGUI::mouseReleaseEvent(QMouseEvent* event)
+{
+    m_resizer.mouseReleaseEvent(event);
 }
 
 void DeviceGUI::mouseMoveEvent(QMouseEvent* event)
@@ -243,6 +247,16 @@ void DeviceGUI::mouseMoveEvent(QMouseEvent* event)
         move(event->globalPos() - m_DragPosition);
         event->accept();
     }
+    else
+    {
+        m_resizer.mouseMoveEvent(event);
+    }
+}
+
+void DeviceGUI::leaveEvent(QEvent* event)
+{
+    m_resizer.leaveEvent(event);
+    QMdiSubWindow::leaveEvent(event);
 }
 
 void DeviceGUI::openChangeDeviceDialog()
diff --git a/sdrgui/device/devicegui.h b/sdrgui/device/devicegui.h
index d4899c84d..69831084b 100644
--- a/sdrgui/device/devicegui.h
+++ b/sdrgui/device/devicegui.h
@@ -26,6 +26,7 @@
 #include <QWidget>
 
 #include "gui/channeladddialog.h"
+#include "gui/framelesswindowresizer.h"
 #include "export.h"
 
 class QCloseEvent;
@@ -76,9 +77,11 @@ public:
     void setChannelNames(const QStringList& channelNames) { m_channelAddDialog.addChannelNames(channelNames); }
 
 protected:
-    void closeEvent(QCloseEvent *event);
-    void mousePressEvent(QMouseEvent* event);
-    void mouseMoveEvent(QMouseEvent* event);
+    void closeEvent(QCloseEvent *event) override;
+    void leaveEvent(QEvent *event) override;
+    void mousePressEvent(QMouseEvent* event) override;
+    void mouseReleaseEvent(QMouseEvent* event) override;
+    void mouseMoveEvent(QMouseEvent* event) override;
     void resetContextMenuType() { m_contextMenuType = ContextMenuNone; }
 
     DeviceType m_deviceType;
@@ -114,12 +117,12 @@ private:
     QHBoxLayout *m_topLayout;
     QHBoxLayout *m_centerLayout;
     QHBoxLayout *m_bottomLayout;
-    QSizeGrip *m_sizeGripTopRight;
     QSizeGrip *m_sizeGripBottomRight;
     bool m_drag;
     QPoint m_DragPosition;
     int m_currentDeviceIndex; //!< Index in device plugins registrations
     ChannelAddDialog m_channelAddDialog;
+    FramelessWindowResizer m_resizer;
 
 private slots:
     void activateSettingsDialog();
diff --git a/sdrgui/feature/featuregui.cpp b/sdrgui/feature/featuregui.cpp
index b4d587b8e..764e8706b 100644
--- a/sdrgui/feature/featuregui.cpp
+++ b/sdrgui/feature/featuregui.cpp
@@ -34,7 +34,8 @@ FeatureGUI::FeatureGUI(QWidget *parent) :
     QMdiSubWindow(parent),
     m_featureIndex(0),
     m_contextMenuType(ContextMenuNone),
-    m_drag(false)
+    m_drag(false),
+    m_resizer(this)
 {
     qDebug("FeatureGUI::FeatureGUI");
     setWindowFlags(windowFlags() | Qt::FramelessWindowHint);
@@ -89,7 +90,7 @@ FeatureGUI::FeatureGUI(QWidget *parent) :
     m_statusLabel->setToolTip("Feature status");
 
     m_layouts = new QVBoxLayout();
-    m_layouts->setContentsMargins(0, 4, 0, 4);
+    m_layouts->setContentsMargins(m_resizer.m_gripSize, 4, m_resizer.m_gripSize, 4);
     m_layouts->setSpacing(2);
 
     m_topLayout = new QHBoxLayout();
@@ -102,10 +103,6 @@ FeatureGUI::FeatureGUI(QWidget *parent) :
     m_topLayout->addWidget(m_moveButton);
     m_topLayout->addWidget(m_shrinkButton);
     m_topLayout->addWidget(m_closeButton);
-    m_sizeGripTopRight = new QSizeGrip(this);
-    m_sizeGripTopRight->setStyleSheet("QSizeGrip { background-color: rgb(128, 128, 128); width: 10px; height: 10px; }");
-    m_sizeGripTopRight->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
-    m_topLayout->addWidget(m_sizeGripTopRight, 0, Qt::AlignTop | Qt::AlignRight);
 
     m_centerLayout = new QHBoxLayout();
     m_centerLayout->addWidget(&m_rollupContents);
@@ -114,7 +111,6 @@ FeatureGUI::FeatureGUI(QWidget *parent) :
     m_bottomLayout->setContentsMargins(0, 0, 0, 0);
     m_bottomLayout->addWidget(m_statusLabel);
     m_sizeGripBottomRight = new QSizeGrip(this);
-    m_sizeGripBottomRight->setStyleSheet("QSizeGrip { background-color: rgb(128, 128, 128); width: 10px; height: 10px; }");
     m_sizeGripBottomRight->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
     // m_bottomLayout->addStretch(1);
     m_bottomLayout->addWidget(m_sizeGripBottomRight, 0, Qt::AlignBottom | Qt::AlignRight);
@@ -147,7 +143,6 @@ FeatureGUI::~FeatureGUI()
     delete m_sizeGripBottomRight;
     delete m_bottomLayout;
     delete m_centerLayout;
-    delete m_sizeGripTopRight;
     delete m_topLayout;
     delete m_layouts;
     delete m_statusLabel;
@@ -176,6 +171,15 @@ void FeatureGUI::mousePressEvent(QMouseEvent* event)
         m_DragPosition = event->globalPos() - pos();
         event->accept();
     }
+    else
+    {
+        m_resizer.mousePressEvent(event);
+    }
+}
+
+void FeatureGUI::mouseReleaseEvent(QMouseEvent* event)
+{
+    m_resizer.mouseReleaseEvent(event);
 }
 
 void FeatureGUI::mouseMoveEvent(QMouseEvent* event)
@@ -185,6 +189,16 @@ void FeatureGUI::mouseMoveEvent(QMouseEvent* event)
         move(event->globalPos() - m_DragPosition);
         event->accept();
     }
+    else
+    {
+        m_resizer.mouseMoveEvent(event);
+    }
+}
+
+void FeatureGUI::leaveEvent(QEvent* event)
+{
+    m_resizer.leaveEvent(event);
+    QMdiSubWindow::leaveEvent(event);
 }
 
 void FeatureGUI::activateSettingsDialog()
diff --git a/sdrgui/feature/featuregui.h b/sdrgui/feature/featuregui.h
index 1c7f3e894..0d8e5ae78 100644
--- a/sdrgui/feature/featuregui.h
+++ b/sdrgui/feature/featuregui.h
@@ -21,6 +21,7 @@
 #include <QMdiSubWindow>
 #include <QMap>
 
+#include "gui/framelesswindowresizer.h"
 #include "gui/rollupcontents.h"
 #include "export.h"
 
@@ -65,9 +66,11 @@ public:
     void setDisplayedame(const QString& name);
 
 protected:
-    void closeEvent(QCloseEvent *event);
-    void mousePressEvent(QMouseEvent* event);
-    void mouseMoveEvent(QMouseEvent* event);
+    void closeEvent(QCloseEvent *event) override;
+    void leaveEvent(QEvent *event) override;
+    void mousePressEvent(QMouseEvent* event) override;
+    void mouseReleaseEvent(QMouseEvent* event) override;
+    void mouseMoveEvent(QMouseEvent* event) override;
     void resetContextMenuType() { m_contextMenuType = ContextMenuNone; }
 
     int m_featureIndex;
@@ -94,11 +97,11 @@ private:
     QHBoxLayout *m_topLayout;
     QHBoxLayout *m_centerLayout;
     QHBoxLayout *m_bottomLayout;
-    QSizeGrip *m_sizeGripTopRight;
     QSizeGrip *m_sizeGripBottomRight;
     bool m_drag;
     QPoint m_DragPosition;
     QMap<QWidget*, int> m_heightsMap;
+    FramelessWindowResizer m_resizer;
 
 private slots:
     void activateSettingsDialog();
diff --git a/sdrgui/gui/framelesswindowresizer.cpp b/sdrgui/gui/framelesswindowresizer.cpp
new file mode 100644
index 000000000..97119938c
--- /dev/null
+++ b/sdrgui/gui/framelesswindowresizer.cpp
@@ -0,0 +1,236 @@
+///////////////////////////////////////////////////////////////////////////////////
+// Copyright (C) 2022 Jon Beniston, M7RCE                                        //
+//                                                                               //
+// This program is free software; you can redistribute it and/or modify          //
+// it under the terms of the GNU General Public License as published by          //
+// the Free Software Foundation as version 3 of the License, or                  //
+// (at your option) any later version.                                           //
+//                                                                               //
+// This program is distributed in the hope that it will be useful,               //
+// but WITHOUT ANY WARRANTY; without even the implied warranty of                //
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the                  //
+// GNU General Public License V3 for more details.                               //
+//                                                                               //
+// You should have received a copy of the GNU General Public License             //
+// along with this program. If not, see <http://www.gnu.org/licenses/>.          //
+///////////////////////////////////////////////////////////////////////////////////
+
+#include <QGuiApplication>
+#include <QLayout>
+
+#include "framelesswindowresizer.h"
+
+FramelessWindowResizer::FramelessWindowResizer(QWidget *widget) :
+    m_widget(widget),
+    m_vResizing(false),
+    m_hResizing(false),
+    m_vMove(false),
+    m_hMove(false),
+    m_cursor(nullptr),
+    m_vCursor(Qt::SizeVerCursor),
+    m_hCursor(Qt::SizeHorCursor),
+    m_bCursor(Qt::SizeBDiagCursor),
+    m_fCursor(Qt::SizeFDiagCursor)
+{
+}
+
+bool FramelessWindowResizer::mouseOnTopBorder(QPoint pos) const
+{
+    return (pos.y() >= 0) && (pos.y() < m_gripSize);
+}
+
+bool FramelessWindowResizer::mouseOnBottomBorder(QPoint pos) const
+{
+    return (pos.y() > m_widget->height() - 1 - m_gripSize) && (pos.y() < m_widget->height());
+}
+
+bool FramelessWindowResizer::mouseOnLeftBorder(QPoint pos) const
+{
+    return (pos.x() >= 0) && (pos.x() < m_gripSize);
+}
+
+bool FramelessWindowResizer::mouseOnRightBorder(QPoint pos) const
+{
+    return (pos.x() > m_widget->width() - 1 - m_gripSize) && (pos.x() < m_widget->width());
+}
+
+bool FramelessWindowResizer::mouseOnBorder(QPoint pos) const
+{
+    return mouseOnTopBorder(pos) || mouseOnBottomBorder(pos)
+        || mouseOnLeftBorder(pos) || mouseOnRightBorder(pos);
+}
+
+void FramelessWindowResizer::setCursor(const QCursor &cursor)
+{
+    if (m_cursor != &cursor)
+    {
+        if (m_cursor != nullptr) {
+            QGuiApplication::restoreOverrideCursor();
+        }
+        QGuiApplication::setOverrideCursor(cursor);
+        m_cursor = &cursor;
+    }
+}
+
+void FramelessWindowResizer::clearCursor()
+{
+    if (m_cursor)
+    {
+        QGuiApplication::restoreOverrideCursor();
+        m_cursor = nullptr;
+    }
+}
+
+void FramelessWindowResizer::mousePressEvent(QMouseEvent* event)
+{
+    if ((event->buttons() & Qt::LeftButton) && mouseOnBorder(event->pos()))
+    {
+        if (mouseOnTopBorder(event->pos()) || mouseOnBottomBorder(event->pos())) {
+            m_vResizing = true;
+        }
+        if (mouseOnLeftBorder(event->pos()) || mouseOnRightBorder(event->pos())) {
+            m_hResizing = true;
+        }
+        if (mouseOnTopBorder(event->pos()))
+        {
+            m_vMove = true;
+            // Calculate difference between mouse position and top left corner of widget
+            m_mouseOffsetToPos = event->globalPos() - m_widget->pos();
+        }
+        if (mouseOnLeftBorder(event->pos()))
+        {
+            m_hMove = true;
+            m_mouseOffsetToPos = event->globalPos() - m_widget->pos();
+        }
+        // Save coords of bottom/right of widget
+        m_origBottomRight.setX(m_widget->pos().x() + m_widget->width());
+        m_origBottomRight.setY(m_widget->pos().y() + m_widget->height());
+
+        m_initialMousePos = event->globalPos();
+        m_initRect = m_widget->rect();
+
+        event->accept();
+    }
+}
+
+void FramelessWindowResizer::mouseReleaseEvent(QMouseEvent* event)
+{
+    if (!(event->buttons() & Qt::LeftButton))
+    {
+        m_vResizing = false;
+        m_hResizing = false;
+        m_vMove = false;
+        m_hMove = false;
+    }
+}
+
+void FramelessWindowResizer::leaveEvent(QEvent* event)
+{
+    clearCursor();
+}
+
+void FramelessWindowResizer::mouseMoveEvent(QMouseEvent* event)
+{
+    if (m_vResizing || m_hResizing)
+    {
+
+        int x, y;
+
+        // Calculate resize requested
+        int w = m_initRect.width();
+        int h = m_initRect.height();
+        QPoint delta = event->globalPos() - m_initialMousePos;
+
+        x = delta.x();
+        y = delta.y();
+        if (m_hMove) {
+            x = -x;
+        }
+        if (m_vMove) {
+            y = -y;
+        }
+        if (m_hResizing) {
+            w += x;
+        }
+        if (m_vResizing) {
+            h += y;
+        }
+        QSize reqSize(w, h);
+
+        // Get min and max size we can resize to
+        QSize minSize, maxSize;
+        if (m_widget->layout())
+        {
+            minSize = m_widget->layout()->minimumSize();
+            maxSize = m_widget->layout()->maximumSize();
+        }
+        else
+        {
+            minSize = m_widget->minimumSize();
+            maxSize = m_widget->maximumSize();
+        }
+
+        // Limit requested to size to allowed min/max
+        QSize size = reqSize;
+        size = size.expandedTo(minSize);
+        size = size.boundedTo(maxSize);
+
+        // Move
+        if (m_vMove || m_hMove)
+        {
+            if (m_hMove)
+            {
+                x = event->globalPos().x() - m_mouseOffsetToPos.x();
+
+                if (x + minSize.width() > m_origBottomRight.x()) {
+                    x = m_origBottomRight.x() - minSize.width();
+                }
+            }
+            else
+            {
+                x = m_widget->pos().x();
+            }
+            if (m_vMove)
+            {
+                y = event->globalPos().y() - m_mouseOffsetToPos.y();
+
+                if (y + minSize.height() > m_origBottomRight.y()) {
+                    y = m_origBottomRight.y() - minSize.height();
+                }
+            }
+            else
+            {
+                y = m_widget->pos().y();
+            }
+            m_widget->move(x, y);
+        }
+
+        m_widget->resize(size);
+        event->accept();
+    }
+    else
+    {
+        QPoint pos = event->pos();
+        if (mouseOnBorder(pos))
+        {
+            // Set cursor according to edge or corner mouse is over
+            if (mouseOnTopBorder(pos) && mouseOnRightBorder(pos)) {
+                setCursor(m_bCursor);
+            } else if (mouseOnTopBorder(pos) && mouseOnLeftBorder(pos)) {
+                setCursor(m_fCursor);
+            } else if (mouseOnBottomBorder(pos) && mouseOnRightBorder(pos)) {
+                setCursor(m_fCursor);
+            } else if (mouseOnBottomBorder(pos) && mouseOnLeftBorder(pos)) {
+                setCursor(m_bCursor);
+            } else if (mouseOnTopBorder(pos) || mouseOnBottomBorder(pos)) {
+                setCursor(m_vCursor);
+            } else if (mouseOnLeftBorder(pos) || mouseOnRightBorder(pos)) {
+                setCursor(m_hCursor);
+            }
+        }
+        else
+        {
+            clearCursor();
+        }
+    }
+}
diff --git a/sdrgui/gui/framelesswindowresizer.h b/sdrgui/gui/framelesswindowresizer.h
new file mode 100644
index 000000000..931b36c63
--- /dev/null
+++ b/sdrgui/gui/framelesswindowresizer.h
@@ -0,0 +1,70 @@
+///////////////////////////////////////////////////////////////////////////////////
+// Copyright (C) 2022 Jon Beniston, M7RCE                                        //
+//                                                                               //
+// This program is free software; you can redistribute it and/or modify          //
+// it under the terms of the GNU General Public License as published by          //
+// the Free Software Foundation as version 3 of the License, or                  //
+// (at your option) any later version.                                           //
+//                                                                               //
+// This program is distributed in the hope that it will be useful,               //
+// but WITHOUT ANY WARRANTY; without even the implied warranty of                //
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the                  //
+// GNU General Public License V3 for more details.                               //
+//                                                                               //
+// You should have received a copy of the GNU General Public License             //
+// along with this program. If not, see <http://www.gnu.org/licenses/>.          //
+///////////////////////////////////////////////////////////////////////////////////
+
+#ifndef SDRGUI_FRAMELESSWINDOWRESIZER_H_
+#define SDRGUI_FRAMELESSWINDOWRESIZER_H_
+
+#include "export.h"
+
+#include <QEvent>
+#include <QMouseEvent>
+#include <QWidget>
+
+// Class to allow frameless windows (Qt::FramelessWindowHint) to be resized
+// by clicking and draging on the border
+// The window needs to forward the mousePressEvent, mouseReleaseEvent, mouseMoveEvent
+// and leaveEvent events to this class
+class SDRGUI_API FramelessWindowResizer
+{
+private:
+    QWidget *m_widget;        // Widget to be resized
+    bool m_vResizing;         // Whether we are resizing vertically
+    bool m_hResizing;
+    bool m_vMove;             // Whether we are moving vertically
+    bool m_hMove;
+    QPoint m_mouseOffsetToPos;// Offset from mouse position to top/left of window
+    QPoint m_initialMousePos; // Position of mouse when resize started
+    QRect m_initRect;         // Dimensions of widget when resize started
+    QPoint m_origBottomRight; // So we do not "push" the widget when resizing from top or left
+    const QCursor *m_cursor;  // Current cursor
+
+    const QCursor m_vCursor;  // Ideally static, but when run:
+    const QCursor m_hCursor;  // QPixmap: Must construct a QGuiApplication before a QPixmap
+    const QCursor m_bCursor;
+    const QCursor m_fCursor;
+
+public:
+    FramelessWindowResizer(QWidget *widget);
+    void mousePressEvent(QMouseEvent* event);
+    void mouseReleaseEvent(QMouseEvent* event);
+    void mouseMoveEvent(QMouseEvent* event);
+    void leaveEvent(QEvent* event);
+
+    const int m_gripSize = 3;   // Size in pixels of the border of the window that can be clicked in to resize it
+
+protected:
+    bool mouseOnTopBorder(QPoint pos) const;
+    bool mouseOnBottomBorder(QPoint pos) const;
+    bool mouseOnLeftBorder(QPoint pos) const;
+    bool mouseOnRightBorder(QPoint pos) const;
+    bool mouseOnBorder(QPoint pos) const;
+    void setCursor(const QCursor &cursor);
+    void clearCursor();
+
+};
+
+#endif // SDRGUI_FRAMELESSWINDOWRESIZER_H_
diff --git a/sdrgui/mainspectrum/mainspectrumgui.cpp b/sdrgui/mainspectrum/mainspectrumgui.cpp
index 2567bdc41..d87da6e9e 100644
--- a/sdrgui/mainspectrum/mainspectrumgui.cpp
+++ b/sdrgui/mainspectrum/mainspectrumgui.cpp
@@ -36,7 +36,8 @@ MainSpectrumGUI::MainSpectrumGUI(GLSpectrum *spectrum, GLSpectrumGUI *spectrumGU
     m_spectrumGUI(spectrumGUI),
     m_deviceType(DeviceRx),
     m_deviceSetIndex(0),
-    m_drag(false)
+    m_drag(false),
+    m_resizer(this)
 {
     qDebug("MainSpectrumGUI::MainSpectrumGUI: %p", parent);
     setWindowFlags(windowFlags() | Qt::FramelessWindowHint);
@@ -92,7 +93,7 @@ MainSpectrumGUI::MainSpectrumGUI(GLSpectrum *spectrum, GLSpectrumGUI *spectrumGU
     // m_statusLabel->setToolTip("Spectrum status");
 
     m_layouts = new QVBoxLayout();
-    m_layouts->setContentsMargins(0, 4, 0, 4);
+    m_layouts->setContentsMargins(m_resizer.m_gripSize, 4, m_resizer.m_gripSize, 4);
     m_layouts->setSpacing(0);
 
     m_topLayout = new QHBoxLayout();
@@ -105,10 +106,6 @@ MainSpectrumGUI::MainSpectrumGUI(GLSpectrum *spectrum, GLSpectrumGUI *spectrumGU
     m_topLayout->addWidget(m_moveButton);
     m_topLayout->addWidget(m_shrinkButton);
     m_topLayout->addWidget(m_hideButton);
-    m_sizeGripTopRight = new QSizeGrip(this);
-    m_sizeGripTopRight->setStyleSheet("QSizeGrip { background-color: rgb(128, 128, 128); width: 10px; height: 10px; }");
-    m_sizeGripTopRight->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
-    m_topLayout->addWidget(m_sizeGripTopRight, 0, Qt::AlignTop | Qt::AlignRight);
 
     m_spectrumLayout = new QHBoxLayout();
     m_spectrumLayout->addWidget(spectrum);
@@ -120,7 +117,6 @@ MainSpectrumGUI::MainSpectrumGUI(GLSpectrum *spectrum, GLSpectrumGUI *spectrumGU
     m_bottomLayout->setContentsMargins(0, 0, 0, 0);
     m_bottomLayout->addWidget(m_statusLabel);
     m_sizeGripBottomRight = new QSizeGrip(this);
-    m_sizeGripBottomRight->setStyleSheet("QSizeGrip { background-color: rgb(128, 128, 128); width: 10px; height: 10px; }");
     m_sizeGripBottomRight->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
     //m_bottomLayout->addStretch(1);
     m_bottomLayout->addWidget(m_sizeGripBottomRight, 0, Qt::AlignBottom | Qt::AlignRight);
@@ -151,7 +147,6 @@ MainSpectrumGUI::~MainSpectrumGUI()
     delete m_bottomLayout;
     delete m_spectrumGUILayout;
     delete m_spectrumLayout;
-    delete m_sizeGripTopRight;
     delete m_topLayout;
     delete m_layouts;
     delete m_statusLabel;
@@ -180,6 +175,15 @@ void MainSpectrumGUI::mousePressEvent(QMouseEvent* event)
         m_DragPosition = event->globalPos() - pos();
         event->accept();
     }
+    else
+    {
+        m_resizer.mousePressEvent(event);
+    }
+}
+
+void MainSpectrumGUI::mouseReleaseEvent(QMouseEvent* event)
+{
+    m_resizer.mouseReleaseEvent(event);
 }
 
 void MainSpectrumGUI::mouseMoveEvent(QMouseEvent* event)
@@ -189,6 +193,16 @@ void MainSpectrumGUI::mouseMoveEvent(QMouseEvent* event)
         move(event->globalPos() - m_DragPosition);
         event->accept();
     }
+    else
+    {
+        m_resizer.mouseMoveEvent(event);
+    }
+}
+
+void MainSpectrumGUI::leaveEvent(QEvent* event)
+{
+    m_resizer.leaveEvent(event);
+    QMdiSubWindow::leaveEvent(event);
 }
 
 void MainSpectrumGUI::showHelp()
diff --git a/sdrgui/mainspectrum/mainspectrumgui.h b/sdrgui/mainspectrum/mainspectrumgui.h
index 859e61678..9adf00a91 100644
--- a/sdrgui/mainspectrum/mainspectrumgui.h
+++ b/sdrgui/mainspectrum/mainspectrumgui.h
@@ -22,6 +22,7 @@
 #include <QByteArray>
 
 #include "util/messagequeue.h"
+#include "gui/framelesswindowresizer.h"
 #include "export.h"
 
 class GLSpectrum;
@@ -82,16 +83,21 @@ private:
     QHBoxLayout *m_spectrumLayout;
     QHBoxLayout *m_spectrumGUILayout;
     QHBoxLayout *m_bottomLayout;
-    QSizeGrip *m_sizeGripTopRight;
     QSizeGrip *m_sizeGripBottomRight;
     bool m_drag;
     QPoint m_DragPosition;
+    FramelessWindowResizer m_resizer;
     static const int m_MinimumWidth = 360;
     static const int m_MinimumHeight = 200 + 20 + 10 + 4*22 + 5;
 
-    void closeEvent(QCloseEvent *event);
-    void mousePressEvent(QMouseEvent* event);
-    void mouseMoveEvent(QMouseEvent* event);
+protected:
+    void closeEvent(QCloseEvent *event) override;
+    void leaveEvent(QEvent *event) override;
+    void mousePressEvent(QMouseEvent* event) override;
+    void mouseReleaseEvent(QMouseEvent* event) override;
+    void mouseMoveEvent(QMouseEvent* event) override;
+
+private:
     bool isOnMovingPad();
     QString getDeviceTypeColor();
     QString getDeviceTypeTag();