KConfigWidgets

kcommandbar.cpp
1/*
2 SPDX-FileCopyrightText: 2021 Waqar Ahmed <waqar.17a@gmail.com>
3
4 SPDX-License-Identifier: LGPL-2.0-or-later
5*/
6#include "kcommandbar.h"
7#include "kcommandbarmodel_p.h"
8#include "kconfigwidgets_debug.h"
9
10#include <QAction>
11#include <QCoreApplication>
12#include <QGraphicsOpacityEffect>
13#include <QHeaderView>
14#include <QKeyEvent>
15#include <QLabel>
16#include <QLineEdit>
17#include <QMainWindow>
18#include <QMenu>
19#include <QPainter>
20#include <QPointer>
21#include <QScreen>
22#include <QSortFilterProxyModel>
23#include <QStatusBar>
24#include <QStyledItemDelegate>
25#include <QTextLayout>
26#include <QToolBar>
27#include <QTreeView>
28#include <QVBoxLayout>
29
30#include <KConfigGroup>
31#include <KFuzzyMatcher>
32#include <KLocalizedString>
33#include <KSharedConfig>
34
35static QRect getCommandBarBoundingRect(KCommandBar *commandBar)
36{
37 QWidget *parentWidget = commandBar->parentWidget();
38 Q_ASSERT(parentWidget);
39
40 const QMainWindow *mainWindow = qobject_cast<const QMainWindow *>(parentWidget);
41 if (!mainWindow) {
42 return parentWidget->geometry();
43 }
44
45 QRect boundingRect = mainWindow->contentsRect();
46
47 // exclude the menu bar from the bounding rect
48 if (const QWidget *menuWidget = mainWindow->menuWidget()) {
49 if (!menuWidget->isHidden()) {
50 boundingRect.setTop(boundingRect.top() + menuWidget->height());
51 }
52 }
53
54 // exclude the status bar from the bounding rect
55 if (const QStatusBar *statusBar = mainWindow->findChild<QStatusBar *>()) {
56 if (!statusBar->isHidden()) {
57 boundingRect.setBottom(boundingRect.bottom() - statusBar->height());
58 }
59 }
60
61 // exclude any undocked toolbar from the bounding rect
62 const QList<QToolBar *> toolBars = mainWindow->findChildren<QToolBar *>();
63 for (QToolBar *toolBar : toolBars) {
64 if (toolBar->isHidden() || toolBar->isFloating()) {
65 continue;
66 }
67
68 switch (mainWindow->toolBarArea(toolBar)) {
70 boundingRect.setTop(std::max(boundingRect.top(), toolBar->geometry().bottom()));
71 break;
73 boundingRect.setRight(std::min(boundingRect.right(), toolBar->geometry().left()));
74 break;
76 boundingRect.setBottom(std::min(boundingRect.bottom(), toolBar->geometry().top()));
77 break;
79 boundingRect.setLeft(std::max(boundingRect.left(), toolBar->geometry().right()));
80 break;
81 default:
82 break;
83 }
84 }
85
86 return boundingRect;
87}
88
89// BEGIN CommandBarFilterModel
90class CommandBarFilterModel final : public QSortFilterProxyModel
91{
92public:
93 CommandBarFilterModel(QObject *parent = nullptr)
95 {
97 m_hasActionsWithIcons = false;
98 });
99 }
100
101 bool hasActionsWithIcons() const
102 {
103 return m_hasActionsWithIcons;
104 }
105
106 Q_SLOT void setFilterString(const QString &string)
107 {
108 // MUST reset the model here, we want to repopulate
109 // invalidateFilter() will not work here
111 m_pattern = string;
113 }
114
115protected:
116 bool lessThan(const QModelIndex &sourceLeft, const QModelIndex &sourceRight) const override
117 {
118 const int scoreLeft = sourceLeft.data(KCommandBarModel::Score).toInt();
119 const int scoreRight = sourceRight.data(KCommandBarModel::Score).toInt();
120 if (scoreLeft == scoreRight) {
121 const QString textLeft = sourceLeft.data().toString();
122 const QString textRight = sourceRight.data().toString();
123
124 return textRight.localeAwareCompare(textLeft) < 0;
125 }
126
127 return scoreLeft < scoreRight;
128 }
129
130 bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override
131 {
132 const QModelIndex index = sourceModel()->index(sourceRow, 0, sourceParent);
133
134 bool accept = false;
135 if (m_pattern.isEmpty()) {
136 accept = true;
137 } else {
139 KFuzzyMatcher::Result resAction = KFuzzyMatcher::match(m_pattern, row);
140 sourceModel()->setData(index, resAction.score, KCommandBarModel::Score);
141 accept = resAction.matched;
142 }
143
144 if (accept && !m_hasActionsWithIcons) {
145 m_hasActionsWithIcons |= !index.data(Qt::DecorationRole).isNull();
146 }
147
148 return accept;
149 }
150
151private:
152 QString m_pattern;
153 mutable bool m_hasActionsWithIcons = false;
154};
155// END CommandBarFilterModel
156
157class CommandBarStyleDelegate final : public QStyledItemDelegate
158{
159public:
160 CommandBarStyleDelegate(QObject *parent = nullptr)
162 {
163 }
164
165 /**
166 * Paints a single item's text
167 */
168 static void
169 paintItemText(QPainter *p, const QString &textt, const QRect &rect, const QStyleOptionViewItem &options, QList<QTextLayout::FormatRange> formats)
170 {
171 QString text = options.fontMetrics.elidedText(textt, Qt::ElideRight, rect.width());
172
173 // set formats and font
174 QTextLayout textLayout(text, options.font);
175 formats.append(textLayout.formats());
176 textLayout.setFormats(formats);
177
178 // set alignment, rtls etc
179 QTextOption textOption;
180 textOption.setTextDirection(options.direction);
181 textOption.setAlignment(QStyle::visualAlignment(options.direction, options.displayAlignment));
182 textLayout.setTextOption(textOption);
183
184 // layout the text
185 textLayout.beginLayout();
186
187 QTextLine line = textLayout.createLine();
188 if (!line.isValid()) {
189 return;
190 }
191
192 const int lineWidth = rect.width();
193 line.setLineWidth(lineWidth);
194 line.setPosition(QPointF(0, 0));
195
196 textLayout.endLayout();
197
198 /**
199 * get "Y" so that we can properly V-Center align the text in row
200 */
201 const int y = QStyle::alignedRect(Qt::LeftToRight, Qt::AlignVCenter, textLayout.boundingRect().size().toSize(), rect).y();
202
203 // draw the text
204 const QPointF pos(rect.x(), y);
205 textLayout.draw(p, pos);
206 }
207
208 void paint(QPainter *painter, const QStyleOptionViewItem &opt, const QModelIndex &index) const override
209 {
210 painter->save();
211
212 /**
213 * Draw everything, (widget, icon etc) except the text
214 */
215 QStyleOptionViewItem option = opt;
216 initStyleOption(&option, index);
217 option.text.clear(); // clear old text
218 QStyle *style = option.widget->style();
219 style->drawControl(QStyle::CE_ItemViewItem, &option, painter, option.widget);
220
221 const int hMargin = style->pixelMetric(QStyle::PM_FocusFrameHMargin, &option, option.widget);
222
223 QRect textRect = option.rect;
224
225 const CommandBarFilterModel *model = static_cast<const CommandBarFilterModel *>(index.model());
226 if (model->hasActionsWithIcons()) {
227 const int iconWidth = option.decorationSize.width() + (hMargin * 2);
228 if (option.direction == Qt::RightToLeft) {
229 textRect.adjust(0, 0, -iconWidth, 0);
230 } else {
231 textRect.adjust(iconWidth, 0, 0, 0);
232 }
233 }
234
235 const QString original = index.data().toString();
236 QStringView str = original;
237 int componentIdx = original.indexOf(QLatin1Char(':'));
238 int actionNameStart = 0;
239 if (componentIdx > 0) {
240 actionNameStart = componentIdx + 2;
241 // + 2 because there is a space after colon
242 str = str.mid(actionNameStart);
243 }
244
246 if (componentIdx > 0) {
248 gray.setForeground(option.palette.placeholderText());
249 formats.append({0, componentIdx, gray});
250 }
251
252 QTextCharFormat fmt;
253 fmt.setForeground(option.palette.link());
255
256 /**
257 * Highlight matches from fuzzy matcher
258 */
259 const auto fmtRanges = KFuzzyMatcher::matchedRanges(m_filterString, str);
261 f.setForeground(option.palette.link());
262 formats.reserve(formats.size() + fmtRanges.size());
263 std::transform(fmtRanges.begin(), fmtRanges.end(), std::back_inserter(formats), [f, actionNameStart](const KFuzzyMatcher::Range &fr) {
264 return QTextLayout::FormatRange{fr.start + actionNameStart, fr.length, f};
265 });
266
267 textRect.adjust(hMargin, 0, -hMargin, 0);
268 paintItemText(painter, original, textRect, option, std::move(formats));
269
270 painter->restore();
271 }
272
273public Q_SLOTS:
274 void setFilterString(const QString &text)
275 {
276 m_filterString = text;
277 }
278
279private:
280 QString m_filterString;
281};
282
283class ShortcutStyleDelegate final : public QStyledItemDelegate
284{
285public:
286 ShortcutStyleDelegate(QObject *parent = nullptr)
288 {
289 }
290
291 void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override
292 {
293 // draw background
294 option.widget->style()->drawPrimitive(QStyle::PE_PanelItemViewItem, &option, painter);
295
296 const QString shortcutString = index.data().toString();
297 if (shortcutString.isEmpty()) {
298 return;
299 }
300
301 const ShortcutSegments shortcutSegments = splitShortcut(shortcutString);
302 if (shortcutSegments.isEmpty()) {
303 return;
304 }
305
306 struct Button {
307 int textWidth;
308 QString text;
309 };
310
311 // compute the width of each shortcut segment
312 QList<Button> btns;
313 btns.reserve(shortcutSegments.count());
314 const int hMargin = horizontalMargin(option);
315 for (const QString &text : shortcutSegments) {
316 int textWidth = option.fontMetrics.horizontalAdvance(text);
317 textWidth += 2 * hMargin;
318 btns.append({textWidth, text});
319 }
320
321 int textHeight = option.fontMetrics.lineSpacing();
322 // this happens on gnome so we manually decrease the height a bit
323 if (textHeight == option.rect.height()) {
324 textHeight -= 4;
325 }
326
327 const int y = option.rect.y() + (option.rect.height() - textHeight) / 2;
328 int x;
329 if (option.direction == Qt::RightToLeft) {
330 x = option.rect.x() + hMargin;
331 } else {
332 x = option.rect.right() - shortcutDrawingWidth(option, shortcutSegments, hMargin) - hMargin;
333 }
334
335 painter->save();
336 painter->setPen(option.palette.buttonText().color());
338 for (int i = 0, n = btns.count(); i < n; ++i) {
339 const Button &button = btns.at(i);
340
341 QRect outputRect(x, y, button.textWidth, textHeight);
342
343 // an even element indicates that it is a key
344 if (i % 2 == 0) {
345 painter->save();
346 painter->setPen(Qt::NoPen);
347
348 // draw rounded rect shadow
349 auto shadowRect = outputRect.translated(0, 1);
350 painter->setBrush(option.palette.shadow());
351 painter->drawRoundedRect(shadowRect, 3.0, 3.0);
352
353 // draw rounded rect itself
354 painter->setBrush(option.palette.window());
355 painter->drawRoundedRect(outputRect, 3.0, 3.0);
356
357 painter->restore();
358 }
359
360 // draw shortcut segment
361 painter->drawText(outputRect, Qt::AlignCenter, button.text);
362
363 x += outputRect.width();
364 }
365
366 painter->restore();
367 }
368
369 QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override
370 {
371 if (index.isValid() && index.column() == KCommandBarModel::Column_Shortcut) {
372 const QString shortcut = index.data().toString();
373 if (!shortcut.isEmpty()) {
374 const ShortcutSegments shortcutSegments = splitShortcut(shortcut);
375 if (!shortcutSegments.isEmpty()) {
376 const int hMargin = horizontalMargin(option);
377 int width = shortcutDrawingWidth(option, shortcutSegments, hMargin);
378
379 // add left and right margins
380 width += 2 * hMargin;
381
382 return QSize(width, 0);
383 }
384 }
385 }
386
387 return QStyledItemDelegate::sizeHint(option, index);
388 }
389
390private:
391 using ShortcutSegments = QStringList;
392
393 // split shortcut into segments i.e. will return
394 // ["Ctrl", "+", "A", ", ", "Ctrl", "+", "K"] for "Ctrl+A, Ctrl+K"
395 // twice as fast as using regular expressions
396 static ShortcutSegments splitShortcut(const QString &shortcut)
397 {
398 ShortcutSegments segments;
399 if (!shortcut.isEmpty()) {
400 const int shortcutLength = shortcut.length();
401 int start = 0;
402 for (int i = 0; i < shortcutLength; ++i) {
403 const QChar c = shortcut.at(i);
404 if (c == QLatin1Char('+')) {
405 if (i > start) {
406 segments << shortcut.mid(start, i - start);
407 }
408 segments << shortcut.at(i);
409 start = i + 1;
410 } else if (c == QLatin1Char(',')) {
411 if (i > start) {
412 segments << shortcut.mid(start, i - start);
413 start = i;
414 }
415 const int j = i + 1;
416 if (j < shortcutLength && shortcut.at(j) == QLatin1Char(' ')) {
417 segments << shortcut.mid(start, j - start + 1);
418 i = j;
419 } else {
420 segments << shortcut.at(i);
421 }
422 start = i + 1;
423 }
424 }
425 if (start < shortcutLength) {
426 segments << shortcut.mid(start);
427 }
428
429 // check we have successfully parsed the shortcut
430 if (segments.isEmpty()) {
431 qCWarning(KCONFIG_WIDGETS_LOG) << "Splitting shortcut failed" << shortcut;
432 }
433 }
434
435 return segments;
436 }
437
438 // returns the width needed to draw the shortcut
439 static int shortcutDrawingWidth(const QStyleOptionViewItem &option, const ShortcutSegments &shortcutSegments, int hMargin)
440 {
441 int width = 0;
442 if (!shortcutSegments.isEmpty()) {
443 width = option.fontMetrics.horizontalAdvance(shortcutSegments.join(QString()));
444
445 // add left and right margins for each segment
446 width += shortcutSegments.count() * 2 * hMargin;
447 }
448
449 return width;
450 }
451
452 int horizontalMargin(const QStyleOptionViewItem &option) const
453 {
454 return option.widget->style()->pixelMetric(QStyle::PM_FocusFrameHMargin, &option) + 2;
455 }
456};
457
458// BEGIN KCommandBarPrivate
459class KCommandBarPrivate
460{
461public:
462 QTreeView m_treeView;
463 QLineEdit m_lineEdit;
464 KCommandBarModel m_model;
465 CommandBarFilterModel m_proxyModel;
466
467 /**
468 * selects first item in treeview
469 */
470 void reselectFirst()
471 {
472 const QModelIndex index = m_proxyModel.index(0, 0);
473 m_treeView.setCurrentIndex(index);
474 }
475
476 /**
477 * blocks signals before clearing line edit to ensure
478 * we don't trigger filtering / sorting
479 */
480 void clearLineEdit()
481 {
482 const QSignalBlocker blocker(m_lineEdit);
483 m_lineEdit.clear();
484 }
485
486 void slotReturnPressed(KCommandBar *q);
487
488 void setLastUsedActions();
489
490 QStringList lastUsedActions() const;
491};
492
493void KCommandBarPrivate::slotReturnPressed(KCommandBar *q)
494{
495 auto act = m_proxyModel.data(m_treeView.currentIndex(), Qt::UserRole).value<QAction *>();
496 if (act) {
497 // if the action is a menu, we take all its actions
498 // and reload our dialog with these instead.
499 if (auto menu = act->menu()) {
500 auto menuActions = menu->actions();
502
503 // if there are no actions, trigger load actions
504 // this happens with some menus that are loaded on demand
505 if (menuActions.size() == 0) {
506 Q_EMIT menu->aboutToShow();
507 ag.actions = menu->actions();
508 }
509
510 QString groupName = KLocalizedString::removeAcceleratorMarker(act->text());
511 ag.name = groupName;
512
513 m_model.refresh({ag});
514 reselectFirst();
515 /**
516 * We want the "textChanged" signal here
517 * so that proxy model triggers filtering again
518 * so don't use d->clearLineEdit()
519 */
520 m_lineEdit.clear();
521 return;
522 } else {
523 m_model.actionTriggered(act->text());
524 q->hide();
525 act->trigger();
526 }
527 }
528
529 clearLineEdit();
530 q->hide();
531 q->deleteLater();
532}
533
534void KCommandBarPrivate::setLastUsedActions()
535{
537 KConfigGroup cg(cfg, QStringLiteral("General"));
538
539 QStringList actionNames = cg.readEntry(QStringLiteral("CommandBarLastUsedActions"), QStringList());
540
541 return m_model.setLastUsedActions(actionNames);
542}
543
544QStringList KCommandBarPrivate::lastUsedActions() const
545{
546 return m_model.lastUsedActions();
547}
548// END KCommandBarPrivate
549
550// BEGIN KCommandBar
552 : QFrame(parent)
553 , d(new KCommandBarPrivate)
554{
556 setProperty("_breeze_force_frame", true);
557
559 e->setColor(palette().color(QPalette::Dark));
560 e->setOffset(0, 4);
561 e->setBlurRadius(48);
563
565 layout->setSpacing(0);
568
569 layout->addWidget(&d->m_lineEdit);
570 d->m_lineEdit.setClearButtonEnabled(true);
571 d->m_lineEdit.addAction(QIcon::fromTheme(QStringLiteral("search")), QLineEdit::LeadingPosition);
572 d->m_lineEdit.setFrame(false);
573 d->m_lineEdit.setTextMargins(QMargins() + style()->pixelMetric(QStyle::PM_ButtonMargin));
574 setFocusProxy(&d->m_lineEdit);
575
576 layout->addWidget(&d->m_treeView);
577 d->m_treeView.setTextElideMode(Qt::ElideLeft);
578 d->m_treeView.setUniformRowHeights(true);
579 d->m_treeView.setProperty("_breeze_borders_sides", QVariant::fromValue(QFlags(Qt::TopEdge)));
580
581 CommandBarStyleDelegate *delegate = new CommandBarStyleDelegate(this);
582 ShortcutStyleDelegate *del = new ShortcutStyleDelegate(this);
583 d->m_treeView.setItemDelegateForColumn(KCommandBarModel::Column_Command, delegate);
584 d->m_treeView.setItemDelegateForColumn(KCommandBarModel::Column_Shortcut, del);
585
586 connect(&d->m_lineEdit, &QLineEdit::returnPressed, this, [this]() {
587 d->slotReturnPressed(this);
588 });
589 connect(&d->m_lineEdit, &QLineEdit::textChanged, &d->m_proxyModel, &CommandBarFilterModel::setFilterString);
590 connect(&d->m_lineEdit, &QLineEdit::textChanged, delegate, &CommandBarStyleDelegate::setFilterString);
591 connect(&d->m_lineEdit, &QLineEdit::textChanged, this, [this]() {
592 d->m_treeView.viewport()->update();
593 d->reselectFirst();
594 });
595 connect(&d->m_treeView, &QTreeView::clicked, this, [this]() {
596 d->slotReturnPressed(this);
597 });
598
599 d->m_proxyModel.setSourceModel(&d->m_model);
600 d->m_treeView.setSortingEnabled(true);
601 d->m_treeView.setModel(&d->m_proxyModel);
602
603 d->m_treeView.header()->setMinimumSectionSize(0);
604 d->m_treeView.header()->setStretchLastSection(false);
605 d->m_treeView.header()->setSectionResizeMode(KCommandBarModel::Column_Command, QHeaderView::Stretch);
606 d->m_treeView.header()->setSectionResizeMode(KCommandBarModel::Column_Shortcut, QHeaderView::ResizeToContents);
607
609 d->m_treeView.installEventFilter(this);
610 d->m_lineEdit.installEventFilter(this);
611
612 d->m_treeView.setHeaderHidden(true);
613 d->m_treeView.setRootIsDecorated(false);
614 d->m_treeView.setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
615 d->m_treeView.setSelectionMode(QTreeView::SingleSelection);
616
617 QLabel *placeholderLabel = new QLabel;
618 placeholderLabel->setAlignment(Qt::AlignCenter);
620 placeholderLabel->setWordWrap(true);
621 // To match the size of a level 2 Heading/KTitleWidget
622 QFont placeholderLabelFont = placeholderLabel->font();
623 placeholderLabelFont.setPointSize(qRound(placeholderLabelFont.pointSize() * 1.3));
624 placeholderLabel->setFont(placeholderLabelFont);
625 // Match opacity of QML placeholder label component
626 QGraphicsOpacityEffect *opacityEffect = new QGraphicsOpacityEffect(placeholderLabel);
627 opacityEffect->setOpacity(0.5);
628 placeholderLabel->setGraphicsEffect(opacityEffect);
629
630 QHBoxLayout *placeholderLayout = new QHBoxLayout;
631 placeholderLayout->addWidget(placeholderLabel);
632 d->m_treeView.setLayout(placeholderLayout);
633
634 connect(&d->m_proxyModel, &CommandBarFilterModel::modelReset, this, [this, placeholderLabel]() {
635 if (d->m_proxyModel.rowCount() > 0) {
636 placeholderLabel->hide();
637 } else {
638 if (d->m_model.rowCount() == 0) {
639 placeholderLabel->setText(i18n("No commands to display"));
640 } else {
641 placeholderLabel->setText(i18n("No commands matching the filter"));
642 }
643 placeholderLabel->show();
644 }
645 });
646
647 setHidden(true);
648
649 // Migrate last used action config to new location
650 KConfigGroup cg(KSharedConfig::openConfig(), QStringLiteral("General"));
651 if (cg.hasKey("CommandBarLastUsedActions")) {
652 const QStringList actionNames = cg.readEntry("CommandBarLastUsedActions", QStringList());
653
654 KConfigGroup stateCg(KSharedConfig::openStateConfig(), QStringLiteral("General"));
655 stateCg.writeEntry(QStringLiteral("CommandBarLastUsedActions"), actionNames);
656
657 cg.deleteEntry(QStringLiteral("CommandBarLastUsedActions"));
658 }
659}
660
661/**
662 * Destructor defined here to make unique_ptr work
663 */
665{
666 auto lastUsedActions = d->lastUsedActions();
668 KConfigGroup cg(cfg, QStringLiteral("General"));
669 cg.writeEntry("CommandBarLastUsedActions", lastUsedActions);
670
671 // Explicitly remove installed event filters of children of d-pointer
672 // class, otherwise while KCommandBar is being torn down, an event could
673 // fire and the eventFilter() accesses d, which would cause a crash
674 // bug 452527
675 d->m_treeView.removeEventFilter(this);
676 d->m_lineEdit.removeEventFilter(this);
677}
678
680{
681 // First set last used actions in the model
682 d->setLastUsedActions();
683
684 d->m_model.refresh(actions);
685 d->reselectFirst();
686
687 show();
688 setFocus();
689}
690
691void KCommandBar::show()
692{
693 const QRect boundingRect = getCommandBarBoundingRect(this);
694
695 static constexpr int minWidth = 500;
696 const int maxWidth = boundingRect.width();
697 const int preferredWidth = maxWidth / 2.4;
698
699 static constexpr int minHeight = 250;
700 const int maxHeight = boundingRect.height();
701 const int preferredHeight = maxHeight / 2;
702
703 const QSize size{std::min(maxWidth, std::max(preferredWidth, minWidth)), std::min(maxHeight, std::max(preferredHeight, minHeight))};
704
706
707 // set the position to the top-center of the parent
708 // just below the menubar/toolbar (if any)
709 const QPoint position{boundingRect.center().x() - size.width() / 2, boundingRect.y() + 6};
710 move(position);
711
713}
714
715bool KCommandBar::eventFilter(QObject *obj, QEvent *event)
716{
717 if (event->type() == QEvent::KeyPress || event->type() == QEvent::ShortcutOverride) {
718 QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
719 if (obj == &d->m_lineEdit) {
720 const int key = keyEvent->key();
721 const bool forward2list = (key == Qt::Key_Up) || (key == Qt::Key_Down) || (key == Qt::Key_PageUp) || (key == Qt::Key_PageDown);
722 if (forward2list) {
723 QCoreApplication::sendEvent(&d->m_treeView, event);
724 return true;
725 }
726 } else if (obj == &d->m_treeView) {
727 const int key = keyEvent->key();
728 const bool forward2input = (key != Qt::Key_Up) && (key != Qt::Key_Down) && (key != Qt::Key_PageUp) && (key != Qt::Key_PageDown)
729 && (key != Qt::Key_Tab) && (key != Qt::Key_Backtab);
730 if (forward2input) {
731 QCoreApplication::sendEvent(&d->m_lineEdit, event);
732 return true;
733 }
734 }
735
736 if (keyEvent->key() == Qt::Key_Escape) {
737 hide();
738 deleteLater();
739 return true;
740 }
741 }
742
743 // hide on focus out, if neither input field nor list have focus!
744 else if (event->type() == QEvent::FocusOut && isVisible() && !(d->m_lineEdit.hasFocus() || d->m_treeView.hasFocus())) {
745 d->clearLineEdit();
746 deleteLater();
747 hide();
748 return true;
749 }
750
751 // handle resizing
752 if (parent() == obj && event->type() == QEvent::Resize) {
753 show();
754 }
755
756 return QWidget::eventFilter(obj, event);
757}
758// END KCommandBar
759
760#include "moc_kcommandbar.cpp"
A hud style menu which allows listing and executing actions.
Definition kcommandbar.h:48
KCommandBar(QWidget *parent)
constructor
void setActions(const QList< ActionGroup > &actions)
actions is a list of {GroupName, QAction}.
~KCommandBar() override
Destructor defined here to make unique_ptr work.
void writeEntry(const char *key, const char *value, WriteConfigFlags pFlags=Normal)
static QString removeAcceleratorMarker(const QString &label)
static KSharedConfig::Ptr openConfig(const QString &fileName=QString(), OpenFlags mode=FullConfig, QStandardPaths::StandardLocation type=QStandardPaths::GenericConfigLocation)
static KSharedConfig::Ptr openStateConfig(const QString &fileName=QString())
Q_SCRIPTABLE Q_NOREPLY void start()
KCOREADDONS_EXPORT Result match(QStringView pattern, QStringView str)
KCOREADDONS_EXPORT QList< KFuzzyMatcher::Range > matchedRanges(QStringView pattern, QStringView str, RangeType type=RangeType::FullyMatched)
const QList< QKeySequence > & shortcut(StandardShortcut id)
void modelAboutToBeReset()
void clicked(const QModelIndex &index)
QModelIndex currentIndex() const const
void setCurrentIndex(const QModelIndex &index)
void addWidget(QWidget *widget, int stretch, Qt::Alignment alignment)
bool sendEvent(QObject *receiver, QEvent *event)
int pointSize() const const
void setPointSize(int pointSize)
virtual bool event(QEvent *e) override
void setFrameStyle(int style)
void setBlurRadius(qreal blurRadius)
void setColor(const QColor &color)
void setOffset(const QPointF &ofs)
void setOpacity(qreal opacity)
QIcon fromTheme(const QString &name)
void setAlignment(Qt::Alignment)
void setTextInteractionFlags(Qt::TextInteractionFlags flags)
void setWordWrap(bool on)
void addWidget(QWidget *w)
void setContentsMargins(const QMargins &margins)
virtual void setSpacing(int)
void clear()
void returnPressed()
void textChanged(const QString &text)
void append(QList< T > &&value)
const_reference at(qsizetype i) const const
qsizetype count() const const
void reserve(qsizetype size)
qsizetype size() const const
QWidget * menuWidget() const const
Qt::ToolBarArea toolBarArea(const QToolBar *toolbar) const const
int column() const const
QVariant data(int role) const const
bool isValid() const const
const QAbstractItemModel * model() const const
Q_SLOTQ_SLOT
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
void deleteLater()
virtual bool eventFilter(QObject *watched, QEvent *event)
T findChild(const QString &name, Qt::FindChildOptions options) const const
QList< T > findChildren(Qt::FindChildOptions options) const const
void installEventFilter(QObject *filterObj)
QObject * parent() const const
bool setProperty(const char *name, QVariant &&value)
void drawRoundedRect(const QRect &rect, qreal xRadius, qreal yRadius, Qt::SizeMode mode)
void drawText(const QPoint &position, const QString &text)
void restore()
void save()
void setBrush(Qt::BrushStyle style)
void setPen(Qt::PenStyle style)
void setRenderHint(RenderHint hint, bool on)
int x() const const
void adjust(int dx1, int dy1, int dx2, int dy2)
int bottom() const const
QPoint center() const const
int height() const const
int left() const const
int right() const const
void setBottom(int y)
void setLeft(int x)
void setRight(int x)
void setTop(int y)
int top() const const
int width() const const
int x() const const
int y() const const
virtual QVariant data(const QModelIndex &index, int role) const const override
virtual QModelIndex index(int row, int column, const QModelIndex &parent) const const override
qsizetype indexOf(QChar ch, qsizetype from, Qt::CaseSensitivity cs) const const
bool isEmpty() const const
int localeAwareCompare(QStringView s1, QStringView s2)
QStringView mid(qsizetype start, qsizetype length) const const
PM_FocusFrameHMargin
PE_PanelItemViewItem
QRect alignedRect(Qt::LayoutDirection direction, Qt::Alignment alignment, const QSize &size, const QRect &rectangle)
virtual void drawControl(ControlElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget) const const=0
virtual int pixelMetric(PixelMetric metric, const QStyleOption *option, const QWidget *widget) const const=0
Qt::Alignment visualAlignment(Qt::LayoutDirection direction, Qt::Alignment alignment)
virtual void initStyleOption(QStyleOptionViewItem *option, const QModelIndex &index) const const
virtual QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const const override
AlignVCenter
DisplayRole
LeftToRight
ScrollBarAlwaysOff
ElideRight
NoTextInteraction
TopToolBarArea
void keyEvent(KeyAction action, QWidget *widget, Qt::Key key, Qt::KeyboardModifiers modifier, int delay)
void setFontWeight(int weight)
void setForeground(const QBrush &brush)
bool isValid() const const
void setLineWidth(qreal width)
void setPosition(const QPointF &pos)
void setAlignment(Qt::Alignment alignment)
void setTextDirection(Qt::LayoutDirection direction)
QVariant fromValue(T &&value)
bool isNull() const const
int toInt(bool *ok) const const
QString toString() const const
T value() const const
QList< QAction * > actions() const const
QRect contentsRect() const const
void hide()
QLayout * layout() const const
QWidget * parentWidget() const const
void move(const QPoint &)
void setFixedSize(const QSize &s)
void setFocus()
void setFocusProxy(QWidget *w)
void setGraphicsEffect(QGraphicsEffect *effect)
void setLayout(QLayout *layout)
void show()
QStyle * style() const const
bool isVisible() const const
Represents a list of action that belong to the same group.
Definition kcommandbar.h:57
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Mon Nov 18 2024 12:07:46 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.