FreeCAD

Форк
0
/
WorkbenchSelector.cpp 
431 строка · 13.9 Кб
1
/***************************************************************************
2
 *   Copyright (c) 2024 Pierre-Louis Boyer <development[at]Ondsel.com>     *
3
 *                                                                         *
4
 *   This file is part of FreeCAD.                                         *
5
 *                                                                         *
6
 *   FreeCAD is free software: you can redistribute it and/or modify it    *
7
 *   under the terms of the GNU Lesser General Public License as           *
8
 *   published by the Free Software Foundation, either version 2.1 of the  *
9
 *   License, or (at your option) any later version.                       *
10
 *                                                                         *
11
 *   FreeCAD is distributed in the hope that it will be useful, but        *
12
 *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
13
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU      *
14
 *   Lesser General Public License for more details.                       *
15
 *                                                                         *
16
 *   You should have received a copy of the GNU Lesser General Public      *
17
 *   License along with FreeCAD. If not, see                               *
18
 *   <https://www.gnu.org/licenses/>.                                      *
19
 *                                                                         *
20
 ***************************************************************************/
21

22

23
#include "PreCompiled.h"
24

25
#ifndef _PreComp_
26
# include <QAbstractItemView>
27
# include <QActionGroup>
28
# include <QApplication>
29
# include <QMenuBar>
30
# include <QScreen>
31
# include <QStatusBar>
32
# include <QToolBar>
33
# include <QLayout>
34
# include <QTimer>
35
#endif
36

37
#include "Base/Tools.h"
38
#include "Action.h"
39
#include "BitmapFactory.h"
40
#include "Command.h"
41
#include "DlgPreferencesImp.h"
42
#include "MainWindow.h"
43
#include "WorkbenchSelector.h"
44
#include "ToolBarAreaWidget.h"
45

46

47
using namespace Gui;
48

49
WorkbenchComboBox::WorkbenchComboBox(WorkbenchGroup* aGroup, QWidget* parent) : QComboBox(parent)
50
{
51
    setIconSize(QSize(16, 16));
52
    setToolTip(aGroup->toolTip());
53
    setStatusTip(aGroup->action()->statusTip());
54
    setWhatsThis(aGroup->action()->whatsThis());
55
    refreshList(aGroup->getEnabledWbActions());
56
    connect(aGroup, &WorkbenchGroup::workbenchListRefreshed, this, &WorkbenchComboBox::refreshList);
57
    connect(aGroup->groupAction(), &QActionGroup::triggered, this, [this, aGroup](QAction* action) {
58
        setCurrentIndex(aGroup->actions().indexOf(action));
59
    });
60
    connect(this, qOverload<int>(&WorkbenchComboBox::activated), aGroup, [aGroup](int index) {
61
        aGroup->actions()[index]->trigger();
62
    });
63
}
64

65
void WorkbenchComboBox::showPopup()
66
{
67
    int rows = count();
68
    if (rows > 0) {
69
        int height = view()->sizeHintForRow(0);
70
        int maxHeight = QApplication::primaryScreen()->size().height();
71
        view()->setMinimumHeight(qMin(height * rows, maxHeight/2));
72
    }
73

74
    QComboBox::showPopup();
75
}
76

77
void WorkbenchComboBox::refreshList(QList<QAction*> actionList)
78
{
79
    clear();
80

81
    ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Workbenches");
82

83
    auto itemStyle = static_cast<WorkbenchItemStyle>(hGrp->GetInt("WorkbenchSelectorItem", 0));
84

85
    for (QAction* action : actionList) {
86
        QIcon icon = action->icon();
87

88
        if (icon.isNull() || itemStyle == WorkbenchItemStyle::TextOnly) {
89
            addItem(action->text());
90
        }
91
        else if (itemStyle == WorkbenchItemStyle::IconOnly) {
92
            addItem(icon, {}); // empty string to ensure that only icon is displayed
93
        }
94
        else {
95
            addItem(icon, action->text());
96
        }
97

98
        if (action->isChecked()) {
99
            this->setCurrentIndex(this->count() - 1);
100
        }
101
    }
102
}
103

104
WorkbenchTabWidget::WorkbenchTabWidget(WorkbenchGroup* aGroup, QWidget* parent)
105
    : QWidget(parent)
106
    , wbActionGroup(aGroup)
107
{
108
    setToolTip(aGroup->toolTip());
109
    setStatusTip(aGroup->action()->statusTip());
110
    setWhatsThis(aGroup->action()->whatsThis());
111
    setObjectName(QString::fromLatin1("WbTabBar"));
112

113
    tabBar = new WbTabBar(this);
114
    moreButton = new QToolButton(this);
115
    layout = new QBoxLayout(QBoxLayout::LeftToRight, this);
116

117
    layout->setContentsMargins(0, 0, 0, 0);
118
    layout->addWidget(tabBar);
119
    layout->addWidget(moreButton);
120
    layout->setAlignment(moreButton, Qt::AlignCenter);
121

122
    setLayout(layout);
123

124
    moreButton->setIcon(Gui::BitmapFactory().iconFromTheme("list-add"));
125
    moreButton->setToolButtonStyle(Qt::ToolButtonIconOnly);
126
    moreButton->setPopupMode(QToolButton::InstantPopup);
127
    moreButton->setMenu(new QMenu(moreButton));
128
    moreButton->setObjectName(QString::fromLatin1("WbTabBarMore"));
129

130
    if (parent->inherits("QToolBar")) {
131
        // when toolbar is created it is not yet placed in its designated area
132
        // therefore we need to wait a bit and then update layout when it is ready
133
        // this is prone to race conditions, but Qt does not supply any event that
134
        // informs us about toolbar changing its placement.
135
        //
136
        // previous implementation saved that information to user settings and
137
        // restored last layout but this creates issues when default workbench has
138
        // different layout than last visited one
139
        QTimer::singleShot(500, [this]() { updateLayout(); });
140
    }
141

142
    tabBar->setDocumentMode(true);
143
    tabBar->setUsesScrollButtons(true);
144
    tabBar->setDrawBase(true);
145
    tabBar->setIconSize(QSize(16, 16));
146

147
    updateWorkbenchList();
148

149
    connect(aGroup, &WorkbenchGroup::workbenchListRefreshed, this, &WorkbenchTabWidget::updateWorkbenchList);
150
    connect(aGroup->groupAction(), &QActionGroup::triggered, this, &WorkbenchTabWidget::handleWorkbenchSelection);
151
    connect(tabBar, &QTabBar::currentChanged, this, &WorkbenchTabWidget::handleTabChange);
152

153
    if (auto toolBar = qobject_cast<QToolBar*>(parent)) {
154
        connect(toolBar, &QToolBar::topLevelChanged, this, &WorkbenchTabWidget::updateLayout);
155
        connect(toolBar, &QToolBar::orientationChanged, this, &WorkbenchTabWidget::updateLayout);
156
    }
157
}
158

159
inline Qt::LayoutDirection WorkbenchTabWidget::direction() const
160
{
161
    return _direction;
162
}
163

164
void WorkbenchTabWidget::setDirection(Qt::LayoutDirection direction)
165
{
166
    _direction = direction;
167

168
    Q_EMIT directionChanged(direction);
169
}
170

171
inline int WorkbenchTabWidget::temporaryWorkbenchTabIndex() const
172
{
173
    if (direction() == Qt::RightToLeft) {
174
        return 0;
175
    }
176

177
    int nextTabIndex = tabBar->count();
178

179
    return temporaryWorkbenchAction ? nextTabIndex - 1 : nextTabIndex;
180
}
181

182
QAction* WorkbenchTabWidget::workbenchActivateActionByTabIndex(int tabIndex) const
183
{
184
    if (temporaryWorkbenchAction && tabIndex == temporaryWorkbenchTabIndex()) {
185
        return temporaryWorkbenchAction;
186
    }
187

188
    auto it = tabIndexToAction.find(tabIndex);
189

190
    if (it != tabIndexToAction.end()) {
191
        return it->second;
192
    }
193

194
    return nullptr;
195
}
196

197
int WorkbenchTabWidget::tabIndexForWorkbenchActivateAction(QAction* workbenchActivateAction) const
198
{
199
    if (workbenchActivateAction == temporaryWorkbenchAction) {
200
        return temporaryWorkbenchTabIndex();
201
    }
202

203
    return actionToTabIndex.at(workbenchActivateAction);
204
}
205

206
WorkbenchItemStyle Gui::WorkbenchTabWidget::itemStyle() const
207
{
208
    ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Workbenches");
209
    return static_cast<WorkbenchItemStyle>(hGrp->GetInt("WorkbenchSelectorItem", 0));
210
}
211

212
void WorkbenchTabWidget::updateLayout()
213
{
214
    if (!parentWidget()) {
215
        setToolBarArea(Gui::ToolBarArea::TopToolBarArea);
216
        return;
217
    }
218

219
    if (auto toolBar = qobject_cast<QToolBar*>(parentWidget())) {
220
        setSizePolicy(toolBar->sizePolicy());
221
        tabBar->setSizePolicy(toolBar->sizePolicy());
222

223
        if (toolBar->isFloating()) {
224
            setToolBarArea(toolBar->orientation() == Qt::Horizontal ? Gui::ToolBarArea::TopToolBarArea : Gui::ToolBarArea::LeftToolBarArea);
225
            return;
226
        }
227
    }
228

229
    auto toolBarArea = Gui::ToolBarManager::getInstance()->toolBarArea(parentWidget());
230

231
    setToolBarArea(toolBarArea);
232

233
    tabBar->setSelectionBehaviorOnRemove(
234
        direction() == Qt::LeftToRight
235
            ? QTabBar::SelectLeftTab
236
            : QTabBar::SelectRightTab
237
    );
238
}
239

240
void WorkbenchTabWidget::handleWorkbenchSelection(QAction* selectedWorkbenchAction)
241
{
242
    if (wbActionGroup->getDisabledWbActions().contains(selectedWorkbenchAction)) {
243
        if (temporaryWorkbenchAction == selectedWorkbenchAction) {
244
            return;
245
        }
246

247
        setTemporaryWorkbenchTab(selectedWorkbenchAction);
248
    }
249

250
    updateLayout();
251

252
    tabBar->setCurrentIndex(tabIndexForWorkbenchActivateAction(selectedWorkbenchAction));
253
}
254

255
void WorkbenchTabWidget::setTemporaryWorkbenchTab(QAction* workbenchActivateAction)
256
{
257
    auto temporaryTabIndex = temporaryWorkbenchTabIndex();
258

259
    if (temporaryWorkbenchAction) {
260
        temporaryWorkbenchAction = nullptr;
261
        tabBar->removeTab(temporaryTabIndex);
262
    }
263

264
    temporaryWorkbenchAction = workbenchActivateAction;
265

266
    if (!workbenchActivateAction) {
267
        return;
268
    }
269

270
    addWorkbenchTab(workbenchActivateAction, temporaryTabIndex);
271

272
    adjustSize();
273
}
274

275
void WorkbenchTabWidget::handleTabChange(int selectedTabIndex)
276
{
277
    // Prevents from rapid workbench changes on initialization as this can cause
278
    // some serious race conditions.
279
    if (isInitializing) {
280
        return;
281
    }
282

283
    if (auto workbenchActivateAction = workbenchActivateActionByTabIndex(selectedTabIndex)) {
284
        workbenchActivateAction->trigger();
285
    }
286

287
    if (selectedTabIndex != temporaryWorkbenchTabIndex()) {
288
        setTemporaryWorkbenchTab(nullptr);
289
    }
290

291
    adjustSize();
292
}
293

294
void WorkbenchTabWidget::updateWorkbenchList()
295
{
296
    if (isInitializing) {
297
        return;
298
    }
299

300
    tabBar->setItemStyle(itemStyle());
301

302
    // As clearing and adding tabs can cause changing current tab in QTabBar.
303
    // This in turn will cause workbench to change, so we need to prevent
304
    // processing of such events until the QTabBar is fully prepared.
305
    Base::StateLocker lock(isInitializing);
306

307
    actionToTabIndex.clear();
308
    tabIndexToAction.clear();
309

310
    // tabs->clear() (QTabBar has no clear)
311
    for (int i = tabBar->count() - 1; i >= 0; --i) {
312
        tabBar->removeTab(i);
313
    }
314

315
    for (QAction* action : wbActionGroup->getEnabledWbActions()) {
316
        addWorkbenchTab(action);
317
    }
318

319
    if (temporaryWorkbenchAction != nullptr) {
320
        setTemporaryWorkbenchTab(temporaryWorkbenchAction);
321
    }
322

323
    buildPrefMenu();
324
    adjustSize();
325
}
326

327
int WorkbenchTabWidget::addWorkbenchTab(QAction* action, int tabIndex)
328
{
329
    auto itemStyle = this->itemStyle();
330

331
    // if tabIndex is negative we assume that tab must be placed at the end of tabBar (default behavior)
332
    if (tabIndex < 0) {
333
        tabIndex = tabBar->count();
334
    }
335

336
    // for the maps we consider order in which tabs have been added
337
    // that's why here we use tabBar->count() instead of tabIndex
338
    actionToTabIndex[action] = tabBar->count();
339
    tabIndexToAction[tabBar->count()] = action;
340

341
    QIcon icon = action->icon();
342
    if (icon.isNull() || itemStyle == WorkbenchItemStyle::TextOnly) {
343
        tabBar->insertTab(tabIndex, action->text());
344
    }
345
    else if (itemStyle == WorkbenchItemStyle::IconOnly) {
346
        tabBar->insertTab(tabIndex, icon, {}); // empty string to ensure only icon is displayed
347
    }
348
    else {
349
        tabBar->insertTab(tabIndex, icon, action->text());
350
    }
351

352
    tabBar->setTabToolTip(tabIndex, action->toolTip());
353

354
    if (action->isChecked()) {
355
        tabBar->setCurrentIndex(tabIndex);
356
    }
357

358
    return tabIndex;
359
}
360

361
void WorkbenchTabWidget::setToolBarArea(Gui::ToolBarArea area)
362
{
363
    switch (area) {
364
        case Gui::ToolBarArea::LeftToolBarArea:
365
        case Gui::ToolBarArea::RightToolBarArea: {
366
            setDirection(Qt::LeftToRight);
367
            layout->setDirection(direction() == Qt::LeftToRight ? QBoxLayout::TopToBottom : QBoxLayout::BottomToTop);
368
            tabBar->setShape(area == Gui::ToolBarArea::LeftToolBarArea ? QTabBar::RoundedWest : QTabBar::RoundedEast);
369
            break;
370
        }
371

372
        case Gui::ToolBarArea::TopToolBarArea:
373
        case Gui::ToolBarArea::BottomToolBarArea:
374
        case Gui::ToolBarArea::LeftMenuToolBarArea:
375
        case Gui::ToolBarArea::RightMenuToolBarArea:
376
        case Gui::ToolBarArea::StatusBarToolBarArea: {
377
            bool isTop =
378
                area == Gui::ToolBarArea::TopToolBarArea ||
379
                area == Gui::ToolBarArea::LeftMenuToolBarArea ||
380
                area == Gui::ToolBarArea::RightMenuToolBarArea;
381

382
            bool isRightAligned =
383
                area == Gui::ToolBarArea::RightMenuToolBarArea ||
384
                area == Gui::ToolBarArea::StatusBarToolBarArea;
385

386
            setDirection(isRightAligned ? Qt::RightToLeft : Qt::LeftToRight);
387
            layout->setDirection(direction() == Qt::LeftToRight ? QBoxLayout::LeftToRight : QBoxLayout::RightToLeft);
388
            tabBar->setShape(isTop ? QTabBar::RoundedNorth : QTabBar::RoundedSouth);
389
            break;
390
        }
391
        default:
392
            // no-op
393
            break;
394
    }
395

396
    adjustSize();
397
}
398

399
void WorkbenchTabWidget::buildPrefMenu()
400
{
401
    auto menu = moreButton->menu();
402

403
    menu->clear();
404

405
    // Add disabled workbenches, sorted alphabetically.
406
    for (auto action : wbActionGroup->getDisabledWbActions()) {
407
        if (action->text() == QString::fromLatin1("<none>")) {
408
            continue;
409
        }
410

411
        menu->addAction(action);
412
    }
413

414
    menu->addSeparator();
415

416
    QAction* preferencesAction = menu->addAction(tr("Preferences"));
417
    connect(preferencesAction, &QAction::triggered, this, []() {
418
        Gui::Dialog::DlgPreferencesImp cDlg(getMainWindow());
419
        cDlg.activateGroupPage(QString::fromUtf8("Workbenches"), 0);
420
        cDlg.exec();
421
    });
422
}
423

424
void WorkbenchTabWidget::adjustSize()
425
{
426
    QWidget::adjustSize();
427

428
    parentWidget()->adjustSize();
429
}
430

431
#include "moc_WorkbenchSelector.cpp"
432

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.