1
0
mirror of https://github.com/f4exb/sdrangel.git synced 2024-11-25 01:18:38 -05:00

Merge pull request #1541 from srcejon/android

Window arrangement
This commit is contained in:
Edouard Griffiths 2022-12-20 18:55:18 +01:00 committed by GitHub
commit cedd7c20d1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 499 additions and 82 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()
@ -398,24 +406,54 @@ void ChannelGUI::openMoveToDeviceSetDialog()
void ChannelGUI::maximizeWindow()
{
m_disableResize = true;
showMaximized();
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
QList<QOpenGLWidget *> widgets = findChildren<QOpenGLWidget *>();
for (auto widget : widgets) {
widget->update();
// 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
QList<QOpenGLWidget *> widgets = findChildren<QOpenGLWidget *>();
for (auto widget : widgets) {
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)
{
@ -336,24 +340,53 @@ void FeatureGUI::sizeToContents()
void FeatureGUI::maximizeWindow()
{
m_disableResize = true;
showMaximized();
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
QList<QOpenGLWidget *> widgets = findChildren<QOpenGLWidget *>();
for (auto widget : widgets) {
widget->update();
// 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
QList<QOpenGLWidget *> widgets = findChildren<QOpenGLWidget *>();
for (auto widget : widgets) {
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
// 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);
};

View File

@ -23,6 +23,7 @@
#include <QSizeGrip>
#include <QObjectCleanupHandler>
#include <QDesktopServices>
#include <QMdiArea>
#include "mainwindow.h"
#include "gui/glspectrum.h"
@ -38,7 +39,8 @@ MainSpectrumGUI::MainSpectrumGUI(GLSpectrum *spectrum, GLSpectrumGUI *spectrumGU
m_deviceType(DeviceRx),
m_deviceSetIndex(0),
m_drag(false),
m_resizer(this)
m_resizer(this),
m_mdi(nullptr)
{
qDebug("MainSpectrumGUI::MainSpectrumGUI: %p", parent);
setWindowFlags(windowFlags() | Qt::FramelessWindowHint);
@ -88,7 +90,7 @@ MainSpectrumGUI::MainSpectrumGUI(GLSpectrum *spectrum, GLSpectrumGUI *spectrumGU
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);
@ -260,15 +262,42 @@ void MainSpectrumGUI::openMoveToWorkspaceDialog()
void MainSpectrumGUI::maximizeWindow()
{
showMaximized();
// 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
{
showMaximized();
m_shrinkButton->setToolTip("Restore window to normal");
m_maximizeButton->setToolTip("Make window full screen");
}
}
void MainSpectrumGUI::shrinkWindow()
{
qDebug("MainSpectrumGUI::shrinkWindow");
if (isMaximized())
if (m_mdi)
{
showNormal();
m_mdi->addSubWindow(this);
show();
showMaximized();
m_shrinkButton->setToolTip("Restore window to normal");
m_mdi = nullptr;
}
else if (isMaximized())
{
showNormal();
m_shrinkButton->setToolTip("Adjust window to minimum size");
m_maximizeButton->setToolTip("Adjust window to maximum size in workspace");
}
else
{
@ -279,6 +308,7 @@ void MainSpectrumGUI::shrinkWindow()
void MainSpectrumGUI::setTitle(const QString& title)
{
setWindowTitle(title + " Spectrum");
m_titleLabel->setText(title);
}

View File

@ -88,6 +88,7 @@ private:
bool m_drag;
QPoint m_DragPosition;
FramelessWindowResizer m_resizer;
QMdiArea *m_mdi; // Saved pointer to MDI when in full screen mode
static const int m_MinimumWidth = 380;
static const int m_MinimumHeight = 200 + 20 + 10 + 6*22 + 5;