/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
// nothing to do for these if (sName == u"GtkCellRendererPixbuf" || sName == u"GtkCellRendererText"
|| sName == u"GtkCellRendererToggle" || sName == u"GtkTreeSelection") return nullptr;
QObject* pObject = nullptr; // in case a QLayout is created, an additional QWidget parent // will also be created because that is needed for QtInstanceContainer
QWidget* pLayoutParentWidget = nullptr;
if (sName == u"GtkMessageDialog")
{
QMessageBox* pMessageBox = new QMessageBox(pParentWidget);
setMessageDialogProperties(*pMessageBox, rMap);
pObject = pMessageBox;
} elseif (sName == u"GtkAssistant")
{
pObject = new QWizard(pParentWidget);
} elseif (sName == u"GtkBox")
{ // for a QMessageBox, return the existing layout instead of creating a new one if (QMessageBox* pMessageBox = qobject_cast<QMessageBox*>(pParent))
{
pObject = pMessageBox->layout();
assert(pObject && "QMessageBox has no layout");
} else
{
QWidget* pBoxParentWidget = pParentWidget; // Unless this is the direct GtkBox child of a GtkDialog, create a parent widget // that can be used to create a QtInstanceContainer for this box if (!qobject_cast<QDialog*>(pParentWidget))
{
pLayoutParentWidget = new QWidget(pParentWidget);
pBoxParentWidget = pLayoutParentWidget;
}
constbool bVertical = hasOrientationVertical(rMap); if (bVertical)
pObject = new QVBoxLayout(pBoxParentWidget); else
pObject = new QHBoxLayout(pBoxParentWidget);
}
} elseif (sName == u"GtkButtonBox")
{
QWidget* pTopLevel = windowForObject(pParent); if (QMessageBox* pMessageBox = qobject_cast<QMessageBox*>(pTopLevel))
{ // for a QMessageBox, return the existing button box instead of creating a new one
QDialogButtonBox* pButtonBox = QtInstanceDialog::findButtonBox(pMessageBox);
assert(pButtonBox && "Could not find QMessageBox's button box");
pObject = pButtonBox;
// skip adding to layout below, button box is already contained in dialog
pParentLayout = nullptr;
} else
{
QDialogButtonBox* pButtonBox = new QDialogButtonBox(pParentWidget); if (hasOrientationVertical(rMap))
pButtonBox->setOrientation(Qt::Vertical);
pObject = pButtonBox;
}
} elseif (sName == u"GtkButton")
{
QPushButton* pButton = new QPushButton(pParentWidget);
setButtonProperties(*pButton, rMap, pParentWidget);
pObject = pButton;
} elseif (sName == u"GtkCalendar")
{
pObject = new QCalendarWidget(pParentWidget);
} elseif (sName == u"GtkCheckButton")
{
QCheckBox* pCheckBox = new QCheckBox(pParentWidget);
setCheckButtonProperties(*pCheckBox, rMap);
pObject = pCheckBox;
} elseif (sName == u"GtkComboBox" || sName == u"GtkComboBoxText")
{
QComboBox* pComboBox = new QComboBox(pParentWidget);
pComboBox->setEditable(extractEntry(rMap));
pObject = pComboBox;
} elseif (sName == u"GtkDialog")
{
QDialog* pDialog = new QDialog(pParentWidget);
setDialogProperties(*pDialog, rMap);
pObject = pDialog;
} elseif (sName == u"GtkDrawingArea")
{
pObject = new QLabel(pParentWidget);
} elseif (sName == u"GtkEntry")
{
QLineEdit* pLineEdit = new QLineEdit(pParentWidget);
setEntryProperties(*pLineEdit, rMap);
pObject = pLineEdit;
} elseif (sName == u"GtkExpander")
{
pObject = new QtExpander(pParentWidget);
} elseif (sName == u"GtkFrame")
{
pObject = new QGroupBox(pParentWidget);
} elseif (sName == u"GtkGrid")
{
pLayoutParentWidget = new QWidget(pParentWidget);
pObject = new QGridLayout(pLayoutParentWidget);
} elseif (sName == u"GtkIconView")
{
QListView* pListView = new QListView(pParentWidget);
pListView->setModel(new QStandardItemModel(pListView));
pListView->setViewMode(QListView::IconMode);
pObject = pListView;
} elseif (sName == u"GtkImage")
{
QLabel* pLabel = new QLabel(pParentWidget); const OUString sIconName = extractIconName(rMap); if (!sIconName.isEmpty())
{ const Image aImage = loadThemeImage(sIconName); if (!aImage.GetBitmapEx().IsEmpty())
{
pLabel->setPixmap(toQPixmap(aImage));
} else
{ const QIcon aIcon = QIcon::fromTheme(toQString(sIconName));
assert(!aIcon.isNull() && "No icon found for that icon name"); constint nIconSize = QApplication::style()->pixelMetric(QStyle::PM_ButtonIconSize);
pLabel->setPixmap(aIcon.pixmap(nIconSize));
}
}
pObject = pLabel;
} elseif (sName == u"GtkLabel")
{
QLabel* pLabel = new QLabel(pParentWidget);
setLabelProperties(*pLabel, rMap);
extractMnemonicWidget(rId, rMap);
pObject = pLabel;
} elseif (sName == u"GtkLevelBar" || sName == u"GtkProgressBar")
{
QProgressBar* pProgressBar = new QProgressBar(pParentWidget); // don't show text (progress in percent) by default
pProgressBar->setTextVisible(false);
pObject = pProgressBar;
} elseif (sName == u"GtkLinkButton")
{
QtHyperlinkLabel* pLabel = new QtHyperlinkLabel(pParentWidget); if (rMap.contains(u"label"_ustr))
pLabel->setDisplayText(toQString(rMap[u"label"_ustr])); if (rMap.contains(u"uri"_ustr))
pLabel->setUri(toQString(rMap[u"uri"_ustr]));
// nothing else to do, return tree view parent for the widget return pTreeView;
} elseif (sName == u"GtkViewport")
{ // GtkViewport is an adaptor to make GtkWidgets scrollable // inside a GtkScrolledWindow; no equivalent needed for widgets // inside QScrollArea - just create a simple QWidget
pObject = new QWidget(pParentWidget);
} else
{
SAL_WARN("vcl.qt", "Widget type not supported yet: "
<< OUStringToOString(sName, RTL_TEXTENCODING_UTF8));
assert(false && "Widget type not supported yet");
}
QWidget* pWidget = qobject_cast<QWidget*>(pObject); if (!pWidget)
pWidget = pLayoutParentWidget;
if (QSplitter* pSplitterParent = qobject_cast<QSplitter*>(pParentWidget))
{
assert(pWidget);
pSplitterParent->addWidget(pWidget); // unset to not create a layout below
pParentWidget = nullptr;
} elseif (QTabWidget* pParentTabWidget = qobject_cast<QTabWidget*>(pParentWidget))
{ // remove QTabWidget child widget, set via QTabWidget::addTab instead
assert(pWidget);
pWidget->setParent(nullptr); // initially, add tab with empty label, QtBuilder::applyTabChildProperties will evaluate actual one
pParentTabWidget->addTab(pWidget, QStringLiteral()); // unset pParentWidget to not create a layout below
pParentWidget = nullptr;
} elseif (QToolBar* pParentToolBar = qobject_cast<QToolBar*>(pParentWidget))
{
pParentToolBar->addWidget(pWidget); // unset pParentWidget to not create a layout below
pParentWidget = nullptr;
} elseif (QtExpander* pExpander = qobject_cast<QtExpander*>(pParentWidget))
{ // set the content (not the label) child as the expander's widget if (sType != "label")
{
pExpander->setContentWidget(pWidget); // erase "visible" property, QtExpander shows/hides the widget as needed
rMap.erase("visible");
}
}
if (pWidget)
{ if (!pParentLayout && pParentWidget)
{ // if the parent is a widget, use the widget's layout, and ensure it has one set
pParentLayout = pParentWidget->layout(); if (!pParentLayout)
pParentLayout = new QVBoxLayout(pParentWidget);
}
// add widget to parent layout if (pParentLayout)
pParentLayout->addWidget(pWidget);
#if QT_VERSION >= QT_VERSION_CHECK(6, 9, 0) // Set GtkBuilder ID as accessible ID
pWidget->setAccessibleIdentifier(toQString(rId)); #endif
} elseif (QLayout* pLayout = qobject_cast<QLayout*>(pObject))
{ // add layout to parent layout if (QBoxLayout* pParentBoxLayout = qobject_cast<QBoxLayout*>(pParentLayout))
pParentBoxLayout->addLayout(pLayout); elseif (QGridLayout* pParentGridLayout = qobject_cast<QGridLayout*>(pParentLayout))
pParentGridLayout->addLayout(pLayout, pParentGridLayout->rowCount(), 0);
}
if (pObject)
pObject->setObjectName(toQString(rId));
if (pWidget)
m_aWidgets[rId] = pWidget;
return pObject;
}
void QtBuilder::tweakInsertedChild(QObject* pParent, QObject* pCurrentChild, std::string_view sType,
std::string_view sInternalChild)
{ if (sInternalChild == "entry" && qobject_cast<QComboBox*>(pParent))
{ // an editable GtkComboBox has an internal GtkEntry child, // but QComboBox doesn't need a separate widget for it, so // delete it
deleteObject(pCurrentChild);
}
if (sType == "label")
{ if (QLabel* pLabel = qobject_cast<QLabel*>(pCurrentChild))
{ if (QGroupBox* pGroupBox = qobject_cast<QGroupBox*>(pParent))
{ // GtkFrame has a `child-type="label"` child for the GtkFrame label // in the GtkBuilder .ui file, s. https://docs.gtk.org/gtk3/class.Frame.html // For QGroupBox, the title can be set directly. Therefore, take over the // title from the label and delete the separate label widget again
pGroupBox->setTitle(pLabel->text());
deleteObject(pLabel);
} elseif (QtExpander* pExpander = qobject_cast<QtExpander*>(pParent))
{ // GtkExpander has a `child-type="label"` child for the expander label // in the GtkBuilder .ui file, s. https://docs.gtk.org/gtk3/class.Expander.html // For QtExpander, the (button) text can be set directly. Therefore, take over // text from the label and delete the separate label widget again
pExpander->setText(pLabel->text());
deleteObject(pLabel);
}
}
}
if (QScrollArea* pScrollAreaParent = qobject_cast<QScrollArea*>(pParent))
{ if (QAbstractScrollArea* pScrollArea = qobject_cast<QAbstractScrollArea*>(pCurrentChild))
{ // if the child provides scrolling capabilities itself, it doesn't need // another scroll area parent -> mark parent scroll area for removal
m_aWidgetReplacements.emplace_back(pScrollAreaParent, pScrollArea);
} else
{ // set as the scroll area's widget
QWidget* pCurrentWidget = nullptr; if (pCurrentChild->isWidgetType())
pCurrentWidget = static_cast<QWidget*>(pCurrentChild); else
pCurrentWidget = static_cast<QLayout*>(pCurrentChild)->parentWidget();
assert(pCurrentWidget);
pScrollAreaParent->setWidget(pCurrentWidget);
}
}
if (QDialog* pDialog = qobject_cast<QDialog*>(pCurrentChild))
{ // no action needed for QMessageBox, where the default button box is used // and button click is handled in QtInstanceMessageDialog if (!qobject_cast<QMessageBox*>(pDialog))
{ if (QDialogButtonBox* pButtonBox = QtInstanceDialog::findButtonBox(pDialog))
{ // ensure that button box is the last item in QDialog's layout // (that seems to be implicitly the case for GtkDialog in GTK)
QLayout* pLayout = pDialog->layout();
assert(pLayout && "dialog has no layout");
pLayout->removeWidget(pButtonBox);
pLayout->addWidget(pButtonBox);
void QtBuilder::setRadioButtonGroup(const OUString& rRadioButtonId, const OUString& rRadioGroupId)
{ // insert all buttons into a button group owned by button whose ID matches the group's
QRadioButton* pGroupOwner = get<QRadioButton>(rRadioGroupId);
assert(pGroupOwner && "No radio button with the given group name");
QRadioButton* pRadioButton = get<QRadioButton>(rRadioButtonId);
assert(pRadioButton && "No radio button with given ID");
pButtonGroup->addButton(pRadioButton);
void QtBuilder::setMenuActionGroup(QMenu* pMenu, QAction* pAction, const OUString& rRadioGroupId)
{ // use QActionGroup owned by and set as property for the QMenu
QActionGroup* pActionGroup = nullptr; const OString sPropertyKey = OUString(u"ACTIONGROUP::"_ustr + rRadioGroupId).toUtf8();
QVariant aVariant = pMenu->property(sPropertyKey.getStr()); if (aVariant.isValid())
{
assert(aVariant.canConvert<QActionGroup*>());
pActionGroup = aVariant.value<QActionGroup*>();
} else
{
pActionGroup = new QActionGroup(pMenu);
pMenu->setProperty(sPropertyKey.getStr(), QVariant::fromValue(pActionGroup)); // insert the menu item which defines the group (whose ID matches the group's ID)
QAction* pIdAction = pMenu->findChild<QAction*>(toQString(rRadioGroupId));
assert(pIdAction && "No action with the ID that the group refers to");
pActionGroup->addAction(pIdAction);
}
// properties not set when there's no explicit GtkGrid in the .ui file, // like for the QGridLayout that's the (implicit) layout of a QMessageBox if (!rPackingProperties.contains(u"left-attach"_ustr)
|| !rPackingProperties.contains(u"top-attach"_ustr)) return;
QWidget* pWidget = nullptr; if (pCurrentChild->isWidgetType())
pWidget = static_cast<QWidget*>(pCurrentChild); else
{
QObject* pParentObject = pCurrentChild->parent();
assert(pParent && "Non-widget (i.e. layout) has no parent"); if (pParentObject->isWidgetType())
pWidget = static_cast<QWidget*>(pParentObject);
}
if (!pWidget) return;
// check parent's parent, due to extra QWidget parents for layouts if (QGridLayout* pGrid = qobject_cast<QGridLayout*>(pParent))
applyGridPackingProperties(pWidget, *pGrid, rPackingProperties); else
SAL_WARN("vcl.qt", "QtBuilder::applyPackingProperties not yet implemented for this case");
}
void QtBuilder::applyTabChildProperties(QObject* pParent, const std::vector<OUString>& rIds,
std::vector<vcl::EnumContext::Context>&,
stringmap& rProperties, stringmap&)
{
QTabWidget* pTabWidget = qobject_cast<QTabWidget*>(pParent);
assert(pTabWidget && "parent must be a QTabWidget");
// set ID and label for the last inserted tab
assert(rProperties.contains(u"label"_ustr) && "Tab has no label");
QtInstanceNotebook::setTabIdAndLabel(*pTabWidget, pTabWidget->count() - 1, rIds.front(),
rProperties.at(u"label"_ustr));
}
// replace old with new widget and mark old widget for deletion if (QLayout* pParentLayout = pParent->layout())
{
std::unique_ptr<QLayoutItem> pOldItem(pParentLayout->replaceWidget(pOldWidget, pNewWidget));
} elseif (QSplitter* pSplitter = qobject_cast<QSplitter*>(pParent))
{ constint nIndex = pSplitter->indexOf(pOldWidget);
assert(nIndex >= 0 && "old widget not a child of the splitter");
pSplitter->replaceWidget(nIndex, pNewWidget);
} else
{
assert(false && "Unhandled case in replaceWidget - not implemented (yet)");
}
if (QDialogButtonBox* pButtonBox = qobject_cast<QDialogButtonBox*>(pParentWidget))
{
pButtonBox->addButton(&rButton, QDialogButtonBox::NoRole);
// for message boxes, avoid implicit standard buttons in addition to those explicitly added if (QMessageBox* pMessageBox = qobject_cast<QMessageBox*>(pParentWidget->window()))
pMessageBox->setStandardButtons(QMessageBox::NoButton);
}
}
void QtBuilder::setEntryProperties(QLineEdit& rLineEdit, stringmap& rProps)
{ auto aIt = rProps.find(u"placeholder-text"_ustr); if (aIt != rProps.end())
rLineEdit.setPlaceholderText(toQString(aIt->second));
aIt = rProps.find(u"visibility"_ustr); if (aIt != rProps.end() && !toBool(aIt->second))
rLineEdit.setEchoMode(QLineEdit::Password);
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.