1
0
mirror of https://github.com/f4exb/sdrangel.git synced 2024-12-23 01:55:48 -05:00

Add buttons to stack MDI windows vertically and put in tabs.

Use right click to auto-stack sub-windows, rather than having a
dedicated button.
Allow maximize button to make window full screen, if already maximized.
Add title to device windows, for when displayed in tabs.
Add menu button to workspace toolbar, for Android only, to avoid having
menu bar, which takes up a lot of space.
Add configuration presets button to workspace toolbar.
Add icons for window arangement
This commit is contained in:
Jon Beniston 2022-12-20 16:10:11 +00:00
parent 7f720a369e
commit d1c67c971e
10 changed files with 464 additions and 78 deletions

View File

@ -65,6 +65,13 @@ QByteArray Configuration::serialize() const
s.writeBool(301 + i, m_workspaceAutoStackOptions[i]);
}
nitems = m_workspaceTabSubWindowsOptions.size() < 99 ? m_workspaceTabSubWindowsOptions.size() : 99;
s.writeS32(400, nitems);
for (int i = 0; i < nitems; i++) {
s.writeBool(401 + i, m_workspaceTabSubWindowsOptions[i]);
}
return s.final();
}
@ -113,6 +120,14 @@ bool Configuration::deserialize(const QByteArray& data)
d.readBool(301 + i, &m_workspaceAutoStackOptions.back());
}
d.readS32(400, &nitems, 0);
for (int i = 0; i < nitems; i++)
{
m_workspaceTabSubWindowsOptions.push_back(true);
d.readBool(401 + i, &m_workspaceTabSubWindowsOptions.back());
}
return true;
}
else
@ -128,4 +143,5 @@ void Configuration::clearData()
m_featureSetPreset.clearFeatures();
m_workspaceGeometries.clear();
m_workspaceAutoStackOptions.clear();
m_workspaceTabSubWindowsOptions.clear();
}

View File

@ -52,6 +52,8 @@ public:
const QList<QByteArray>& getWorkspaceGeometries() const { return m_workspaceGeometries; }
QList<bool>& getWorkspaceAutoStackOptions() { return m_workspaceAutoStackOptions; }
const QList<bool>& getWorkspaceAutoStackOptions() const { return m_workspaceAutoStackOptions; }
QList<bool>& getWorkspaceTabSubWindowsOptions() { return m_workspaceTabSubWindowsOptions; }
const QList<bool>& getWorkspaceTabSubWindowsOptions() const { return m_workspaceTabSubWindowsOptions; }
FeatureSetPreset& getFeatureSetPreset() { return m_featureSetPreset; }
const FeatureSetPreset& getFeatureSetPreset() const { return m_featureSetPreset; }
QList<Preset>& getDeviceSetPresets() { return m_deviceSetPresets; }
@ -76,6 +78,7 @@ private:
QString m_description;
QList<QByteArray> m_workspaceGeometries;
QList<bool> m_workspaceAutoStackOptions;
QList<bool> m_workspaceTabSubWindowsOptions;
FeatureSetPreset m_featureSetPreset;
QList<Preset> m_deviceSetPresets;
};

View File

@ -26,11 +26,13 @@
#include <QObjectCleanupHandler>
#include <QDesktopServices>
#include <QOpenGLWidget>
#include <QMdiArea>
#include "mainwindow.h"
#include "gui/workspaceselectiondialog.h"
#include "gui/devicesetselectiondialog.h"
#include "gui/rollupcontents.h"
#include "gui/dialogpositioner.h"
#include "channelgui.h"
@ -42,7 +44,8 @@ ChannelGUI::ChannelGUI(QWidget *parent) :
m_contextMenuType(ContextMenuNone),
m_drag(false),
m_resizer(this),
m_disableResize(false)
m_disableResize(false),
m_mdi(nullptr)
{
qDebug("ChannelGUI::ChannelGUI");
setWindowFlags(windowFlags() | Qt::FramelessWindowHint);
@ -92,7 +95,7 @@ ChannelGUI::ChannelGUI(QWidget *parent) :
m_maximizeButton->setFixedSize(20, 20);
QIcon maximizeIcon(":/maximize.png");
m_maximizeButton->setIcon(maximizeIcon);
m_maximizeButton->setToolTip("Adjust window to maximum size");
m_maximizeButton->setToolTip("Adjust window to maximum size in workspace");
m_hideButton = new QPushButton();
m_hideButton->setFixedSize(20, 20);
@ -311,7 +314,9 @@ void ChannelGUI::onWidgetRolled(QWidget *widget, bool show)
// onWidgetRolled being called twice.
// We need to make sure we don't save widget heights while this occurs. The
// window manager will take care of maximizing/restoring the window size.
if (!m_disableResize)
// We do need to resize when a widget is rolled up, but we also need to avoid
// resizing when a window is maximized when first shown in tabbed layout
if (!m_disableResize && !isMaximized())
{
if (show)
{
@ -379,6 +384,9 @@ void ChannelGUI::sizeToContents()
size.setHeight(size.height() + getAdditionalHeight());
size.setWidth(size.width() + m_resizer.m_gripSize * 2);
setMinimumSize(size);
// Restrict size of window to size of desktop
DialogPositioner::sizeToDesktop(this);
}
void ChannelGUI::duplicateChannel()
@ -397,9 +405,24 @@ void ChannelGUI::openMoveToDeviceSetDialog()
}
void ChannelGUI::maximizeWindow()
{
// If maximize is pressed when maximized, go full screen
if (isMaximized())
{
m_mdi = mdiArea();
if (m_mdi) {
m_mdi->removeSubWindow(this);
}
showNormal(); // If we don't go back to normal first, window doesn't get bigger
showFullScreen();
m_shrinkButton->setToolTip("Adjust window to maximum size in workspace");
}
else
{
m_disableResize = true;
showMaximized();
m_shrinkButton->setToolTip("Restore window to normal");
m_maximizeButton->setToolTip("Make window full screen");
m_disableResize = false;
// QOpenGLWidget widgets don't always paint properly first time after being maximized,
// so force an update. Should really fix why they aren't painted properly in the first place
@ -408,14 +431,29 @@ void ChannelGUI::maximizeWindow()
widget->update();
}
}
}
void ChannelGUI::shrinkWindow()
{
qDebug("ChannelGUI::shrinkWindow");
if (isMaximized())
// If m_normalParentWidget, window was made full screen
if (m_mdi)
{
m_disableResize = true;
showNormal();
m_mdi->addSubWindow(this);
show();
showMaximized();
m_shrinkButton->setToolTip("Restore window to normal");
m_disableResize = false;
m_mdi = nullptr;
}
else if (isMaximized())
{
m_disableResize = true;
showNormal();
m_shrinkButton->setToolTip("Adjust window to minimum size");
m_maximizeButton->setToolTip("Adjust window to maximum size in workspace");
m_disableResize = false;
}
else

View File

@ -141,6 +141,7 @@ private:
QMap<QWidget*, int> m_heightsMap;
FramelessWindowResizer m_resizer;
bool m_disableResize;
QMdiArea *m_mdi; // Saved pointer to MDI when in full screen mode
private slots:
void activateSettingsDialog();

View File

@ -162,7 +162,7 @@ DeviceGUI::DeviceGUI(QWidget *parent) :
m_topLayout->addWidget(m_maximizeButton);
m_topLayout->addWidget(m_closeButton);
m_centerLayout = new QHBoxLayout();
m_centerLayout = new QVBoxLayout();
m_centerLayout->setContentsMargins(0, 0, 0, 0);
m_contents = new QWidget(); // Do not delete! Done in child's destructor with "delete ui"
m_centerLayout->addWidget(m_contents);
@ -412,6 +412,7 @@ void DeviceGUI::deviceSetPresetsDialog()
void DeviceGUI::setTitle(const QString& title)
{
setWindowTitle(title + " Device");
m_titleLabel->setText(title);
}

View File

@ -24,6 +24,7 @@
#include <QString>
#include <QByteArray>
#include <QWidget>
#include <QLabel>
#include "gui/channeladddialog.h"
#include "gui/framelesswindowresizer.h"
@ -32,7 +33,6 @@
class QCloseEvent;
class Message;
class MessageQueue;
class QLabel;
class QPushButton;
class QVBoxLayout;
class QHBoxLayout;
@ -87,6 +87,7 @@ protected:
void mouseMoveEvent(QMouseEvent* event) override;
void resetContextMenuType() { m_contextMenuType = ContextMenuNone; }
int getAdditionalHeight() const { return 22 + 22; }
void setStatus(const QString &status) { m_statusLabel->setText(status); }
DeviceUISet* m_deviceUISet;
DeviceType m_deviceType;
@ -122,7 +123,7 @@ private:
QLabel *m_statusLabel;
QVBoxLayout *m_layouts;
QHBoxLayout *m_topLayout;
QHBoxLayout *m_centerLayout;
QVBoxLayout *m_centerLayout;
QHBoxLayout *m_bottomLayout;
QSizeGrip *m_sizeGripBottomRight;
bool m_drag;

View File

@ -26,6 +26,7 @@
#include <QObjectCleanupHandler>
#include <QDesktopServices>
#include <QOpenGLWidget>
#include <QMdiArea>
#include "mainwindow.h"
#include "gui/workspaceselectiondialog.h"
@ -37,7 +38,8 @@ FeatureGUI::FeatureGUI(QWidget *parent) :
m_contextMenuType(ContextMenuNone),
m_drag(false),
m_resizer(this),
m_disableResize(false)
m_disableResize(false),
m_mdi(nullptr)
{
qDebug("FeatureGUI::FeatureGUI");
setWindowFlags(windowFlags() | Qt::FramelessWindowHint);
@ -87,7 +89,7 @@ FeatureGUI::FeatureGUI(QWidget *parent) :
m_maximizeButton->setFixedSize(20, 20);
QIcon maximizeIcon(":/maximize.png");
m_maximizeButton->setIcon(maximizeIcon);
m_maximizeButton->setToolTip("Adjust window to maximum size");
m_maximizeButton->setToolTip("Adjust window to maximum size in workspace");
m_closeButton = new QPushButton();
m_closeButton->setFixedSize(20, 20);
@ -264,7 +266,9 @@ void FeatureGUI::onWidgetRolled(QWidget *widget, bool show)
// onWidgetRolled being called twice.
// We need to make sure we don't save widget heights while this occurs. The
// window manager will take care of maximizing/restoring the window size.
if (!m_disableResize)
// We do need to resize when a widget is rolled up, but we also need to avoid
// resizing when a window is maximized when first shown in tabbed layout
if (!m_disableResize && !isMaximized())
{
if (show)
{
@ -335,9 +339,24 @@ void FeatureGUI::sizeToContents()
}
void FeatureGUI::maximizeWindow()
{
// If maximize is pressed when maximized, go full screen
if (isMaximized())
{
m_mdi = mdiArea();
if (m_mdi) {
m_mdi->removeSubWindow(this);
}
showNormal(); // If we don't go back to normal first, window doesn't get bigger
showFullScreen();
m_shrinkButton->setToolTip("Adjust window to maximum size in workspace");
}
else
{
m_disableResize = true;
showMaximized();
m_shrinkButton->setToolTip("Restore window to normal");
m_maximizeButton->setToolTip("Make window full screen");
m_disableResize = false;
// QOpenGLWidget widgets don't always paint properly first time after being maximized,
// so force an update. Should really fix why they aren't painted properly in the first place
@ -346,14 +365,28 @@ void FeatureGUI::maximizeWindow()
widget->update();
}
}
}
void FeatureGUI::shrinkWindow()
{
qDebug("FeatureGUI::shrinkWindow");
if (isMaximized())
if (m_mdi)
{
m_disableResize = true;
showNormal();
m_mdi->addSubWindow(this);
show();
showMaximized();
m_shrinkButton->setToolTip("Restore window to normal");
m_disableResize = false;
m_mdi = nullptr;
}
else if (isMaximized())
{
m_disableResize = true;
showNormal();
m_shrinkButton->setToolTip("Adjust window to minimum size");
m_maximizeButton->setToolTip("Adjust window to maximum size in workspace");
m_disableResize = false;
}
else

View File

@ -110,6 +110,7 @@ private:
QMap<QWidget*, int> m_heightsMap;
FramelessWindowResizer m_resizer;
bool m_disableResize;
QMdiArea *m_mdi; // Saved pointer to MDI when in full screen mode
private slots:
void activateSettingsDialog();

View File

@ -27,10 +27,13 @@
#include <QFrame>
#include <QDebug>
#include <QApplication>
#include <QMenu>
#include <QAction>
#include "gui/samplingdevicedialog.h"
#include "gui/rollupcontents.h"
#include "gui/buttonswitch.h"
#include "gui/crightclickenabler.h"
#include "channel/channelgui.h"
#include "feature/featuregui.h"
#include "device/devicegui.h"
@ -42,8 +45,10 @@
Workspace::Workspace(int index, QWidget *parent, Qt::WindowFlags flags) :
QDockWidget(parent, flags),
m_index(index),
m_menuButton(nullptr),
m_featureAddDialog(this),
m_stacking(false),
m_autoStack(false),
m_userChannelMinWidth(0)
{
m_mdi = new QMdiArea(this);
@ -64,6 +69,29 @@ Workspace::Workspace(int index, QWidget *parent, Qt::WindowFlags flags) :
m_titleLabel->setStyleSheet("QLabel { background-color: rgb(128, 128, 128); qproperty-alignment: AlignCenter; }");
m_titleLabel->setText(windowTitle());
#ifdef ANDROID
m_menuButton = new QToolButton();
QIcon menuIcon(":/listing.png");
m_menuButton->setIcon(menuIcon);
m_menuButton->setFixedSize(20, 20);
m_menuButton->setPopupMode(QToolButton::InstantPopup);
#endif
m_configurationPresetsButton = new QPushButton();
QIcon configurationPresetsIcon(":/star.png");
m_configurationPresetsButton->setIcon(configurationPresetsIcon);
m_configurationPresetsButton->setToolTip("Configuration presets");
m_configurationPresetsButton->setFixedSize(20, 20);
m_startStopButton = new ButtonSwitch();
m_startStopButton->setCheckable(true);
updateStartStopButton(false);
m_startStopButton->setFixedSize(20, 20);
m_vline1 = new QFrame();
m_vline1->setFrameShape(QFrame::VLine);
m_vline1->setFrameShadow(QFrame::Sunken);
m_addRxDeviceButton = new QPushButton();
QIcon addRxIcon(":/rx.png");
m_addRxDeviceButton->setIcon(addRxIcon);
@ -82,14 +110,9 @@ Workspace::Workspace(int index, QWidget *parent, Qt::WindowFlags flags) :
m_addMIMODeviceButton->setToolTip("Add MIMO device");
m_addMIMODeviceButton->setFixedSize(20, 20);
m_startStopButton = new ButtonSwitch();
m_startStopButton->setCheckable(true);
updateStartStopButton(false);
m_startStopButton->setFixedSize(20, 20);
m_vline1 = new QFrame();
m_vline1->setFrameShape(QFrame::VLine);
m_vline1->setFrameShadow(QFrame::Sunken);
m_vline2 = new QFrame();
m_vline2->setFrameShape(QFrame::VLine);
m_vline2->setFrameShadow(QFrame::Sunken);
m_addFeatureButton = new QPushButton();
QIcon addFeatureIcon(":/tool_add.png");
@ -103,9 +126,9 @@ Workspace::Workspace(int index, QWidget *parent, Qt::WindowFlags flags) :
m_featurePresetsButton->setToolTip("Feature presets");
m_featurePresetsButton->setFixedSize(20, 20);
m_vline2 = new QFrame();
m_vline2->setFrameShape(QFrame::VLine);
m_vline2->setFrameShadow(QFrame::Sunken);
m_vline3 = new QFrame();
m_vline3->setFrameShape(QFrame::VLine);
m_vline3->setFrameShadow(QFrame::Sunken);
m_cascadeSubWindows = new QPushButton();
QIcon cascadeSubWindowsIcon(":/cascade.png");
@ -119,19 +142,26 @@ Workspace::Workspace(int index, QWidget *parent, Qt::WindowFlags flags) :
m_tileSubWindows->setToolTip("Tile sub windows");
m_tileSubWindows->setFixedSize(20, 20);
m_stackSubWindows = new QPushButton("S");
//QIcon stackSubWindowsIcon(":/stack.png"); // FIXME
//m_stackSubWindows->setIcon(stackSubWindowsIcon);
m_stackSubWindows->setToolTip("Stack sub windows");
m_stackSubWindows->setFixedSize(20, 20);
m_stackVerticalSubWindows = new QPushButton();
QIcon stackVerticalSubWindowsIcon(":/stackvertical.png");
m_stackVerticalSubWindows->setIcon(stackVerticalSubWindowsIcon);
m_stackVerticalSubWindows->setToolTip("Stack sub windows vertically");
m_stackVerticalSubWindows->setFixedSize(20, 20);
m_autoStackSubWindows = new ButtonSwitch();
m_autoStackSubWindows->setText("AS");
m_autoStackSubWindows->setCheckable(true);
//QIcon autoStackSubWindowsIcon(":/autostack.png"); // FIXME
//m_autoStackSubWindows->setIcon(autoStackSubWindowsIcon);
m_autoStackSubWindows->setToolTip("Automatically stack sub windows");
m_autoStackSubWindows->setFixedSize(20, 20);
m_stackSubWindows = new QPushButton();
QIcon stackSubWindowsIcon(":/stackcolumns.png");
m_stackSubWindows->setIcon(stackSubWindowsIcon);
m_stackSubWindows->setToolTip("Stack sub windows in columns. Right click to stack automatically.");
m_stackSubWindows->setFixedSize(20, 20);
CRightClickEnabler *stackSubWindowsRightClickEnabler = new CRightClickEnabler(m_stackSubWindows);
connect(stackSubWindowsRightClickEnabler, &CRightClickEnabler::rightClick, this, &Workspace::autoStackSubWindows);
m_tabSubWindows = new ButtonSwitch();
QIcon tabSubWindowsIcon(":/tab.png");
m_tabSubWindows->setIcon(tabSubWindowsIcon);
m_tabSubWindows->setCheckable(true);
m_tabSubWindows->setToolTip("Display sub windows in tabs");
m_tabSubWindows->setFixedSize(20, 20);
m_normalButton = new QPushButton();
QIcon normalIcon(":/dock.png");
@ -146,21 +176,34 @@ Workspace::Workspace(int index, QWidget *parent, Qt::WindowFlags flags) :
m_closeButton->setFixedSize(20, 20);
m_titleBarLayout->addWidget(m_titleLabel);
if (m_menuButton) {
m_titleBarLayout->addWidget(m_menuButton);
}
m_titleBarLayout->addWidget(m_configurationPresetsButton);
m_titleBarLayout->addWidget(m_startStopButton);
m_titleBarLayout->addWidget(m_vline1);
m_titleBarLayout->addWidget(m_addRxDeviceButton);
m_titleBarLayout->addWidget(m_addTxDeviceButton);
m_titleBarLayout->addWidget(m_addMIMODeviceButton);
m_titleBarLayout->addWidget(m_startStopButton);
m_titleBarLayout->addWidget(m_vline1);
m_titleBarLayout->addWidget(m_vline2);
m_titleBarLayout->addWidget(m_addFeatureButton);
m_titleBarLayout->addWidget(m_featurePresetsButton);
m_titleBarLayout->addWidget(m_vline2);
m_titleBarLayout->addWidget(m_vline3);
m_titleBarLayout->addWidget(m_cascadeSubWindows);
m_titleBarLayout->addWidget(m_tileSubWindows);
m_titleBarLayout->addWidget(m_stackVerticalSubWindows);
m_titleBarLayout->addWidget(m_stackSubWindows);
m_titleBarLayout->addWidget(m_autoStackSubWindows);
m_titleBarLayout->addWidget(m_tabSubWindows);
m_titleBarLayout->addStretch(1);
#ifndef ANDROID
// Can't undock on Android, as windows don't have title bars to allow them to be moved
m_titleBarLayout->addWidget(m_normalButton);
// Don't allow workspaces to be hidden on Android, as if all are hidden, they'll
// be no way to redisplay them, as we currently don't have a main menu bar
m_titleBarLayout->addWidget(m_closeButton);
#else
setFeatures(QDockWidget::NoDockWidgetFeatures);
#endif
setTitleBarWidget(m_titleBar);
QObject::connect(
@ -198,6 +241,13 @@ Workspace::Workspace(int index, QWidget *parent, Qt::WindowFlags flags) :
&Workspace::featurePresetsDialog
);
QObject::connect(
m_configurationPresetsButton,
&QPushButton::clicked,
this,
&Workspace::configurationPresetsDialog
);
QObject::connect(
m_cascadeSubWindows,
&QPushButton::clicked,
@ -212,6 +262,13 @@ Workspace::Workspace(int index, QWidget *parent, Qt::WindowFlags flags) :
&Workspace::tileSubWindows
);
QObject::connect(
m_stackVerticalSubWindows,
&QPushButton::clicked,
this,
&Workspace::stackVerticalSubWindows
);
QObject::connect(
m_stackSubWindows,
&QPushButton::clicked,
@ -227,10 +284,10 @@ Workspace::Workspace(int index, QWidget *parent, Qt::WindowFlags flags) :
);
QObject::connect(
m_autoStackSubWindows,
m_tabSubWindows,
&QPushButton::clicked,
this,
&Workspace::autoStackSubWindows
&Workspace::tabSubWindows
);
QObject::connect(
@ -256,6 +313,17 @@ Workspace::Workspace(int index, QWidget *parent, Qt::WindowFlags flags) :
&Workspace::deviceStateChanged
);
QObject::connect(
m_mdi,
&QMdiArea::subWindowActivated,
this,
&Workspace::subWindowActivated
);
#ifdef ANDROID
m_tabSubWindows->setChecked(true);
tabSubWindows();
#endif
}
Workspace::~Workspace()
@ -263,13 +331,17 @@ Workspace::~Workspace()
qDebug("Workspace::~Workspace");
delete m_closeButton;
delete m_normalButton;
delete m_autoStackSubWindows;
delete m_tabSubWindows;
delete m_stackSubWindows;
delete m_stackVerticalSubWindows;
delete m_tileSubWindows;
delete m_cascadeSubWindows;
delete m_vline3;
delete m_vline2;
delete m_vline1;
delete m_startStopButton;
delete m_configurationPresetsButton;
delete m_menuButton;
delete m_addRxDeviceButton;
delete m_addTxDeviceButton;
delete m_addMIMODeviceButton;
@ -346,18 +418,126 @@ void Workspace::featurePresetsDialog()
emit featurePresetsDialogRequested(p, this);
}
void Workspace::configurationPresetsDialog()
{
emit configurationPresetsDialogRequested();
}
void Workspace::cascadeSubWindows()
{
m_autoStackSubWindows->setChecked(false);
setAutoStackOption(false);
m_tabSubWindows->setChecked(false);
m_mdi->setViewMode(QMdiArea::SubWindowView);
m_mdi->cascadeSubWindows();
}
void Workspace::tileSubWindows()
{
m_autoStackSubWindows->setChecked(false);
setAutoStackOption(false);
m_tabSubWindows->setChecked(false);
m_mdi->setViewMode(QMdiArea::SubWindowView);
m_mdi->tileSubWindows();
}
void Workspace::stackVerticalSubWindows()
{
setAutoStackOption(false);
unmaximizeSubWindows();
m_mdi->setViewMode(QMdiArea::SubWindowView);
// Spacing between windows
const int spacing = 2;
// Categorise windows according to type and calculate min size needed
QList<QMdiSubWindow *> windows = m_mdi->subWindowList(QMdiArea::CreationOrder);
QList<DeviceGUI *> devices;
QList<MainSpectrumGUI *> spectrums;
QList<ChannelGUI *> channels;
QList<FeatureGUI *> features;
int minHeight = 0;
int minWidth = 0;
int nonFixedWindows = 0;
for (auto window : windows)
{
if (window->isVisible() && !window->isMaximized())
{
if (window->inherits("DeviceGUI")) {
devices.append(qobject_cast<DeviceGUI *>(window));
} else if (window->inherits("MainSpectrumGUI")) {
spectrums.append(qobject_cast<MainSpectrumGUI *>(window));
} else if (window->inherits("ChannelGUI")) {
channels.append(qobject_cast<ChannelGUI *>(window));
} else if (window->inherits("FeatureGUI")) {
features.append(qobject_cast<FeatureGUI *>(window));
}
minHeight += window->minimumSizeHint().height() + spacing;
minWidth = std::max(minWidth, window->minimumSizeHint().width());
if (window->sizePolicy().verticalPolicy() != QSizePolicy::Fixed) {
nonFixedWindows++;
}
}
}
// Order windows by device/feature/channel index
orderByIndex(devices);
orderByIndex(spectrums);
orderByIndex(channels);
orderByIndex(features);
// Will we need scroll bars?
QSize mdiSize = m_mdi->size();
bool requiresHScrollBar = minWidth > mdiSize.width();
bool requiresVScrollBar = minHeight > mdiSize.height();
// Reduce available size if scroll bars needed
int sbWidth = qApp->style()->pixelMetric(QStyle::PM_ScrollBarExtent);
if (requiresVScrollBar) {
mdiSize.setWidth(mdiSize.width() - sbWidth);
}
if (requiresHScrollBar) {
mdiSize.setHeight(mdiSize.height() - sbWidth);
}
// Calculate spare vertical space, to be shared between non-fixed windows
int spareSpacePerWindow;
if (requiresVScrollBar) {
spareSpacePerWindow = 0;
} else {
spareSpacePerWindow = (mdiSize.height() - minHeight) / nonFixedWindows;
}
// Now position the windows
int x = 0;
int y = 0;
for (auto window : devices)
{
window->move(x, y);
y += window->size().height() + spacing;
}
for (auto window : spectrums)
{
window->move(x, y);
window->resize(mdiSize.width(), window->minimumSizeHint().height() + spareSpacePerWindow);
y += window->size().height() + spacing;
}
for (auto window : channels)
{
window->move(x, y);
int extra = (window->sizePolicy().verticalPolicy() == QSizePolicy::Fixed) ? 0 : spareSpacePerWindow;
window->resize(mdiSize.width(), window->minimumSizeHint().height() + extra);
y += window->size().height() + spacing;
}
for (auto window : features)
{
window->move(x, y);
int extra = (window->sizePolicy().verticalPolicy() == QSizePolicy::Fixed) ? 0 : spareSpacePerWindow;
window->resize(mdiSize.width(), window->minimumSizeHint().height() + extra);
y += window->size().height() + spacing;
}
}
void Workspace::orderByIndex(QList<ChannelGUI *> &list)
{
std::sort(list.begin(), list.end(),
@ -398,16 +578,36 @@ void Workspace::orderByIndex(QList<MainSpectrumGUI *> &list)
});
}
void Workspace::unmaximizeSubWindows()
{
if (m_tabSubWindows->isChecked())
{
m_tabSubWindows->setChecked(false);
// Unmaximize any maximized windows
QList<QMdiSubWindow *> windows = m_mdi->subWindowList(QMdiArea::CreationOrder);
for (auto window : windows)
{
if (window->isMaximized()) {
window->showNormal();
}
}
}
}
// Try to arrange windows somewhat like in earlier versions of SDRangel
// Devices and fixed size features stacked on left
// Spectrum and expandable features stacked in centre
// Channels stacked on right
void Workspace::stackSubWindows()
{
unmaximizeSubWindows();
// Set a flag so event handler knows if it's this code or the user that
// resizes a window
m_stacking = true;
m_mdi->setViewMode(QMdiArea::SubWindowView);
// Categorise windows according to type
QList<QMdiSubWindow *> windows = m_mdi->subWindowList(QMdiArea::CreationOrder);
QList<DeviceGUI *> devices;
@ -653,7 +853,54 @@ void Workspace::stackSubWindows()
void Workspace::autoStackSubWindows()
{
if (m_autoStackSubWindows->isChecked()) {
setAutoStackOption(!m_autoStack);
}
void Workspace::tabSubWindows()
{
if (m_tabSubWindows->isChecked())
{
// Disable autostack
setAutoStackOption(false);
// Move sub windows out of view, so they can't be seen next to a non-expandible window
// Perhaps there's a better way to do this - showMinimized didn't work
QList<QMdiSubWindow *> windows = m_mdi->subWindowList(QMdiArea::CreationOrder);
for (auto window : windows)
{
if ((window != m_mdi->activeSubWindow()) && ((window->x() != 5000) || (window->y() != 0))) {
window->move(5000, 0);
}
}
m_mdi->setViewMode(QMdiArea::TabbedView);
}
else
{
m_mdi->setViewMode(QMdiArea::SubWindowView);
}
}
void Workspace::subWindowActivated(QMdiSubWindow *activatedWindow)
{
if (activatedWindow && m_tabSubWindows->isChecked())
{
// Move other windows out of the way
QList<QMdiSubWindow *> windows = m_mdi->subWindowList(QMdiArea::CreationOrder);
for (auto window : windows)
{
if ((window != activatedWindow) && ((window->x() != 5000) || (window->y() != 0))) {
window->move(5000, 0);
} else if ((window == activatedWindow) && ((window->x() != 0) || (window->y() != 0))) {
window->move(0, 0);
}
}
}
}
void Workspace::layoutSubWindows()
{
if (m_autoStack) {
stackSubWindows();
}
}
@ -716,7 +963,7 @@ void Workspace::deviceStateChanged(int index, DeviceAPI *deviceAPI)
void Workspace::resizeEvent(QResizeEvent *event)
{
QDockWidget::resizeEvent(event);
autoStackSubWindows();
layoutSubWindows();
}
void Workspace::addToMdiArea(QMdiSubWindow *sub)
@ -725,17 +972,20 @@ void Workspace::addToMdiArea(QMdiSubWindow *sub)
sub->installEventFilter(this);
// Can't use Close event, as it's before window is closed, so
// catch sub-window destroyed signal instead
connect(sub, &QObject::destroyed, this, &Workspace::autoStackSubWindows);
connect(sub, &QObject::destroyed, this, &Workspace::layoutSubWindows);
m_mdi->addSubWindow(sub);
sub->show();
// Auto-stack when sub-window's widgets are rolled up
ChannelGUI *channel = qobject_cast<ChannelGUI *>(sub);
if (channel) {
connect(channel->getRollupContents(), &RollupContents::widgetRolled, this, &Workspace::autoStackSubWindows);
connect(channel->getRollupContents(), &RollupContents::widgetRolled, this, &Workspace::layoutSubWindows);
}
FeatureGUI *feature = qobject_cast<FeatureGUI *>(sub);
if (feature) {
connect(feature->getRollupContents(), &RollupContents::widgetRolled, this, &Workspace::autoStackSubWindows);
connect(feature->getRollupContents(), &RollupContents::widgetRolled, this, &Workspace::layoutSubWindows);
}
if (m_tabSubWindows->isChecked()) {
sub->showMaximized();
}
}
@ -743,14 +993,14 @@ void Workspace::removeFromMdiArea(QMdiSubWindow *sub)
{
m_mdi->removeSubWindow(sub);
sub->removeEventFilter(this);
disconnect(sub, &QObject::destroyed, this, &Workspace::autoStackSubWindows);
disconnect(sub, &QObject::destroyed, this, &Workspace::layoutSubWindows);
ChannelGUI *channel = qobject_cast<ChannelGUI *>(sub);
if (channel) {
disconnect(channel->getRollupContents(), &RollupContents::widgetRolled, this, &Workspace::autoStackSubWindows);
disconnect(channel->getRollupContents(), &RollupContents::widgetRolled, this, &Workspace::layoutSubWindows);
}
FeatureGUI *feature = qobject_cast<FeatureGUI *>(sub);
if (feature) {
disconnect(feature->getRollupContents(), &RollupContents::widgetRolled, this, &Workspace::autoStackSubWindows);
disconnect(feature->getRollupContents(), &RollupContents::widgetRolled, this, &Workspace::layoutSubWindows);
}
}
@ -760,19 +1010,19 @@ bool Workspace::eventFilter(QObject *obj, QEvent *event)
{
QWidget *widget = qobject_cast<QWidget *>(obj);
if (!widget->isMaximized()) {
autoStackSubWindows();
layoutSubWindows();
}
}
else if (event->type() == QEvent::Hide)
{
QWidget *widget = qobject_cast<QWidget *>(obj);
if (!widget->isMaximized()) {
autoStackSubWindows();
layoutSubWindows();
}
}
else if (event->type() == QEvent::Resize)
{
if (!m_stacking && m_autoStackSubWindows->isChecked())
if (!m_stacking && m_autoStack)
{
QWidget *widget = qobject_cast<QWidget *>(obj);
QResizeEvent *resizeEvent = static_cast<QResizeEvent *>(event);
@ -812,12 +1062,38 @@ void Workspace::restoreMdiGeometry(const QByteArray& blob)
bool Workspace::getAutoStackOption() const
{
return m_autoStackSubWindows->isChecked();
return m_autoStack;
}
void Workspace::setAutoStackOption(bool autoStack)
{
m_autoStackSubWindows->doToggle(autoStack);
m_autoStack = autoStack;
if (!m_autoStack)
{
m_stackSubWindows->setStyleSheet(QString("QPushButton{ background-color: %1; }")
.arg(palette().button().color().name()));
}
else
{
m_stackSubWindows->setStyleSheet(QString("QPushButton{ background-color: %1; }")
.arg(palette().highlight().color().darker(150).name()));
stackSubWindows();
}
}
bool Workspace::getTabSubWindowsOption() const
{
return m_tabSubWindows->isChecked();
}
void Workspace::setTabSubWindowsOption(bool tab)
{
m_tabSubWindows->doToggle(tab);
if (tab) {
tabSubWindows();
} else {
m_mdi->setViewMode(QMdiArea::SubWindowView);
}
}
void Workspace::adjustSubWindowsAfterRestore()

View File

@ -28,6 +28,7 @@
class QHBoxLayout;
class QLabel;
class QToolButton;
class QPushButton;
class QMdiArea;
class QMdiSubWindow;
@ -37,7 +38,6 @@ class ChannelGUI;
class FeatureGUI;
class DeviceGUI;
class MainSpectrumGUI;
class SDRGUI_API Workspace : public QDockWidget
{
Q_OBJECT
@ -56,6 +56,8 @@ public:
void restoreMdiGeometry(const QByteArray& blob);
bool getAutoStackOption() const;
void setAutoStackOption(bool autoStack);
bool getTabSubWindowsOption() const;
void setTabSubWindowsOption(bool tab);
QList<QMdiSubWindow *> getSubWindowList() const;
void orderByIndex(QList<ChannelGUI *> &list);
void orderByIndex(QList<FeatureGUI *> &list);
@ -63,21 +65,26 @@ public:
void orderByIndex(QList<MainSpectrumGUI *> &list);
void adjustSubWindowsAfterRestore();
void updateStartStopButton(bool checked);
QToolButton *getMenuButton() const { return m_menuButton; }
private:
int m_index;
QToolButton *m_menuButton;
QPushButton *m_configurationPresetsButton;
ButtonSwitch *m_startStopButton;
QFrame *m_vline1;
QPushButton *m_addRxDeviceButton;
QPushButton *m_addTxDeviceButton;
QPushButton *m_addMIMODeviceButton;
ButtonSwitch *m_startStopButton;
QFrame *m_vline1;
QFrame *m_vline2;
QPushButton *m_addFeatureButton;
QPushButton *m_featurePresetsButton;
QFrame *m_vline2;
QFrame *m_vline3;
QPushButton *m_cascadeSubWindows;
QPushButton *m_tileSubWindows;
QPushButton *m_stackVerticalSubWindows;
QPushButton *m_stackSubWindows;
ButtonSwitch *m_autoStackSubWindows;
ButtonSwitch *m_tabSubWindows;
QWidget *m_titleBar;
QHBoxLayout *m_titleBarLayout;
QLabel *m_titleLabel;
@ -86,8 +93,11 @@ private:
FeatureAddDialog m_featureAddDialog;
QMdiArea *m_mdi;
bool m_stacking; // Set when stackSubWindows() is running
bool m_autoStack; // Automatically stack
int m_userChannelMinWidth; // Minimum width of channels column for stackSubWindows(), set by user resizing a channel window
void unmaximizeSubWindows();
protected:
void resizeEvent(QResizeEvent *event) override;
bool eventFilter(QObject *obj, QEvent *event) override;
@ -98,14 +108,19 @@ private slots:
void addMIMODeviceClicked();
void addFeatureDialog();
void featurePresetsDialog();
void configurationPresetsDialog();
void cascadeSubWindows();
void tileSubWindows();
void stackVerticalSubWindows();
void stackSubWindows();
void autoStackSubWindows();
void tabSubWindows();
void layoutSubWindows();
void startStopClicked(bool checked = false);
void addFeatureEmitted(int featureIndex);
void toggleFloating();
void deviceStateChanged(int index, DeviceAPI *deviceAPI);
void subWindowActivated(QMdiSubWindow *window);
signals:
void addRxDevice(Workspace *inWorkspace, int deviceIndex);
@ -113,6 +128,7 @@ signals:
void addMIMODevice(Workspace *inWorkspace, int deviceIndex);
void addFeature(Workspace*, int);
void featurePresetsDialogRequested(QPoint, Workspace*);
void configurationPresetsDialogRequested();
void startAllDevices(Workspace *inWorkspace);
void stopAllDevices(Workspace *inWorkspace);
};