Add helper for setting tab orders automatically

The helper functions are put into separate files under the Helpers folder.

The helper is used in OptionsWindow and SettingsWindow.

The helper sets the tab order of a widget using its row and column number in the grid layout. The tab orders are assigned first from left to right, then from top to bottom.

If in the future a different custom type of layout is used, or a container other than QTabWidget is used, then the helper functions may need to be changed.
This commit is contained in:
gexgd0419 2024-10-19 23:39:47 +08:00
parent 2fe4d71352
commit 125828e830
7 changed files with 144 additions and 53 deletions

View File

@ -0,0 +1,124 @@
#include "stdafx.h"
#include "TabOrder.h"
// Items to be sorted by row & col
template <class T>
struct ChildItem
{
T item;
int row, col;
template <class U>
ChildItem(U&& item, int row, int col)
: item(std::forward<U>(item)), row(row), col(col) {}
bool operator<(const ChildItem& other) const // make it sortable
{
if (row != other.row)
return row < other.row;
return col < other.col; // sort first by row, then by col
}
};
void GetWidgetsInOrder(QWidget* widget, std::vector<QWidget*>& widgets);
void GetWidgetsInOrder(QLayout* layout, std::vector<QWidget*>& widgets);
// Fills the given list with widgets in the correct tab order (recursively)
void GetWidgetsInOrder(QWidget* widget, std::vector<QWidget*>& widgets)
{
if (widget->focusPolicy() != Qt::FocusPolicy::NoFocus)
{
widgets.push_back(widget); // add the widget itself
}
if (QLayout* layout = widget->layout())
{
// if managed by a layout
GetWidgetsInOrder(layout, widgets);
return;
}
// If not managed by a layout, try to get its child widgets.
// Here only children of QTabWidgets are actually processed.
// More branches will be necessary if there's another type of container widget used.
if (auto* parent = qobject_cast<QTabWidget*>(widget))
{
int cnt = parent->count();
for (int i = 0; i < cnt; i++)
GetWidgetsInOrder(parent->widget(i), widgets);
}
}
// Fills the given list with widgets in the correct tab order (recursively)
void GetWidgetsInOrder(QLayout* layout, std::vector<QWidget*>& widgets)
{
// Get a list of layout items in the layout,
// then sort by rows and columns to get the correct tab order
int cnt = layout->count();
std::vector<ChildItem<QLayoutItem*>> items;
if (QGridLayout* gridLayout = qobject_cast<QGridLayout*>(layout))
{
for (int i = 0; i < cnt; i++)
{
int row, col, rowSpan, colSpan;
gridLayout->getItemPosition(i, &row, &col, &rowSpan, &colSpan);
items.emplace_back(gridLayout->itemAt(i), row, col);
}
}
else if (QFormLayout* formLayout = qobject_cast<QFormLayout*>(layout))
{
for (int i = 0; i < cnt; i++)
{
int row;
QFormLayout::ItemRole role;
formLayout->getItemPosition(i, &row, &role);
items.emplace_back(formLayout->itemAt(i), row, (int)role);
}
}
else
{
// For other types of layouts, preserve the order in the layout
for (int i = 0; i < cnt; i++)
{
items.emplace_back(layout->itemAt(i), 0, i);
}
}
std::stable_sort(items.begin(), items.end());
// process all child layouts/widgets in the sorted order
for (const auto& item : items)
{
if (QLayout* l = item.item->layout())
GetWidgetsInOrder(l, widgets);
else if (QWidget* w = item.item->widget())
GetWidgetsInOrder(w, widgets);
}
}
void SetTabOrder(QWidget* root)
{
std::vector<QWidget*> widgets;
GetWidgetsInOrder(root, widgets);
QWidget* prev = nullptr;
for (QWidget* widget : widgets)
{
if (prev)
QWidget::setTabOrder(prev, widget);
prev = widget;
}
}
void SetTabOrder(QLayout* root)
{
std::vector<QWidget*> widgets;
GetWidgetsInOrder(root, widgets);
QWidget* prev = nullptr;
for (QWidget* widget : widgets)
{
if (prev)
QWidget::setTabOrder(prev, widget);
prev = widget;
}
}

View File

@ -0,0 +1,4 @@
#pragma once
void SetTabOrder(QWidget* root);
void SetTabOrder(QLayout* root);

View File

@ -23,6 +23,7 @@ HEADERS += ./stdafx.h \
./Helpers/StorageInfo.h \
./Helpers/ReadDirectoryChanges.h \
./Helpers/ReadDirectoryChangesPrivate.h \
./Helpers/TabOrder.h \
./Windows/RecoveryWindow.h \
./Windows/PopUpWindow.h \
./Windows/SnapshotsWindow.h \
@ -74,6 +75,7 @@ SOURCES += ./main.cpp \
./Helpers/ReadDirectoryChanges.cpp \
./Helpers/ReadDirectoryChangesPrivate.cpp \
./Helpers/WindowFromPointEx.cpp \
./Helpers/TabOrder.cpp \
./Windows/OptionsWindow.cpp \
./Windows/PopUpWindow.cpp \
./Windows/RecoveryWindow.cpp \

View File

@ -299,6 +299,7 @@
<ClCompile Include="Helpers\ReadDirectoryChanges.cpp" />
<ClCompile Include="Helpers\ReadDirectoryChangesPrivate.cpp" />
<ClCompile Include="Helpers\StorageInfo.cpp" />
<ClCompile Include="Helpers\TabOrder.cpp" />
<ClCompile Include="Helpers\WinAdmin.cpp" />
<ClCompile Include="Helpers\WindowFromPointEx.cpp" />
<ClCompile Include="Helpers\WinHelper.cpp" />
@ -485,6 +486,7 @@
<ClInclude Include="Helpers\ReadDirectoryChanges.h" />
<ClInclude Include="Helpers\ReadDirectoryChangesPrivate.h" />
<ClInclude Include="Helpers\StorageInfo.h" />
<ClInclude Include="Helpers\TabOrder.h" />
<ClInclude Include="Helpers\ThreadSafeQueue.h" />
<ClInclude Include="Helpers\WinAdmin.h" />
<QtMoc Include="Models\MonitorModel.h" />

View File

@ -237,6 +237,9 @@
<ClCompile Include="Windows\ExtractDialog.cpp">
<Filter>Windows</Filter>
</ClCompile>
<ClCompile Include="Helpers\TabOrder.cpp">
<Filter>Helpers</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="stdafx.h">
@ -278,6 +281,9 @@
<ClInclude Include="CustomStyles.h">
<Filter>SandMan</Filter>
</ClInclude>
<ClInclude Include="Helpers\TabOrder.h">
<Filter>Helpers</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<QtMoc Include="SandMan.h">

View File

@ -8,55 +8,7 @@
#include "../MiscHelpers/Common/SettingsWidgets.h"
#include "Helpers/WinAdmin.h"
#include "../Wizards/TemplateWizard.h"
static bool IsAncestorOf(QObject* container, QObject* obj)
{
while (obj)
{
if (obj == container)
return true;
obj = obj->parent();
}
return false;
}
static QWidgetList GetTabOrder(QObject* container)
{
QWidgetList list;
QWidget* pWidget = container->findChild<QWidget*>();
if (!pWidget)
return list;
list.append(pWidget);
for (QWidget* pPrev = pWidget->previousInFocusChain();
pPrev && IsAncestorOf(container, pPrev);
pPrev = pPrev->previousInFocusChain())
{
list.prepend(pPrev);
}
for (QWidget* pNext = pWidget->nextInFocusChain();
pNext && IsAncestorOf(container, pNext);
pNext = pNext->nextInFocusChain())
{
list.append(pNext);
}
return list;
}
static void RestoreTabOrder(const QWidgetList& list)
{
QWidget* pPrev = nullptr;
for (QWidget* pWidget : list)
{
if (pPrev)
QWidget::setTabOrder(pPrev, pWidget);
pPrev = pWidget;
}
}
#include "Helpers/TabOrder.h"
class NoEditDelegate : public QStyledItemDelegate {
@ -324,14 +276,12 @@ COptionsWindow::COptionsWindow(const QSharedPointer<CSbieIni>& pBox, const QStri
pDummy->setVisible(false);
// merge recovery tabs
QWidgetList tabOrder = GetTabOrder(ui.tabsRecovery);
QWidget* pWidget3 = new QWidget();
pWidget3->setLayout(ui.gridLayout_10);
ui.gridLayout_24->addWidget(pWidget3, 1, 0);
QWidget* pWidget4 = new QWidget();
pWidget4->setLayout(ui.gridLayout_56);
ui.gridLayout_24->addWidget(pWidget4, 2, 0);
RestoreTabOrder(tabOrder);
delete ui.tabsRecovery;
ui.gridLayout_24->setContentsMargins(0, 0, 0, 0);
@ -357,7 +307,6 @@ COptionsWindow::COptionsWindow(const QSharedPointer<CSbieIni>& pBox, const QStri
if (iOptionLayout == 1)
{
// merge stop tabs
tabOrder = GetTabOrder(ui.tabsStop);
QWidget* pWidget1 = new QWidget();
pWidget1->setLayout(ui.gridLayout_57);
ui.gridLayout_17->addWidget(pWidget1, 1, 0);
@ -371,7 +320,6 @@ COptionsWindow::COptionsWindow(const QSharedPointer<CSbieIni>& pBox, const QStri
ui.lblStopOpt->setVisible(false);
ui.lblStopOpt->setProperty("hidden", true);
ui.gridLayout_17->addWidget(pWidget3, 3, 0);
RestoreTabOrder(tabOrder);
delete ui.tabsStop;
ui.gridLayout_17->setContentsMargins(0, 0, 0, 0);
@ -718,6 +666,8 @@ COptionsWindow::COptionsWindow(const QSharedPointer<CSbieIni>& pBox, const QStri
this->addAction(pSetTree);
}
m_pSearch->setPlaceholderText(tr("Search for options"));
SetTabOrder(this);
}
void COptionsWindow::ApplyIniEditFont()

View File

@ -17,6 +17,7 @@
#include <qfontdialog.h>
#include <QJsonDocument>
#include <QJsonObject>
#include "Helpers/TabOrder.h"
#include <windows.h>
@ -628,6 +629,8 @@ CSettingsWindow::CSettingsWindow(QWidget* parent)
this->addAction(pSetTree);
}
m_pSearch->setPlaceholderText(tr("Search for settings"));
SetTabOrder(this);
}
void CSettingsWindow::ApplyIniEditFont()