diff --git a/app/layoutsDelegates/activitycmbboxdelegate.cpp b/app/layoutsDelegates/activitycmbboxdelegate.cpp index eabc5901..0c758e48 100644 --- a/app/layoutsDelegates/activitycmbboxdelegate.cpp +++ b/app/layoutsDelegates/activitycmbboxdelegate.cpp @@ -1,187 +1,195 @@ /* * Copyright 2017-2018 Michail Vourlakos * * This file is part of Latte-Dock * * Latte-Dock is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * Latte-Dock is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "activitycmbboxdelegate.h" #include "../settingsdialog.h" #include #include #include #include #include #include #include #include #include ActivityCmbBoxDelegate::ActivityCmbBoxDelegate(QObject *parent) : QItemDelegate(parent) { auto *settingsDialog = qobject_cast(parent); if (settingsDialog) { m_settingsDialog = settingsDialog; } } QWidget *ActivityCmbBoxDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const { QComboBox *editor = new QComboBox(parent); QStringList assignedActivities = index.model()->data(index, Qt::UserRole).toStringList(); QStringList availableActivities = m_settingsDialog->availableActivities(); QStringList activities = m_settingsDialog->activities(); QStringList shownActivities; foreach (auto activity, activities) { if (assignedActivities.contains(activity) || availableActivities.contains(activity)) { shownActivities.append(activity); } } for (unsigned int i = 0; i < shownActivities.count(); ++i) { KActivities::Info info(shownActivities[i]); QString indicator = " "; if (assignedActivities.contains(shownActivities[i])) { indicator = QString::fromUtf8("\u2714") + " "; } if (info.state() != KActivities::Info::Invalid) { editor->addItem(QIcon::fromTheme(info.icon()), QString(indicator + info.name()), QVariant(shownActivities[i])); } } connect(editor, static_cast(&QComboBox::activated), [ = ](int index) { editor->clearFocus(); }); return editor; } void ActivityCmbBoxDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const { QComboBox *comboBox = static_cast(editor); QStringList assignedActivities = index.model()->data(index, Qt::UserRole).toStringList(); int pos = -1; if (assignedActivities.count() > 0) { pos = comboBox->findData(QVariant(assignedActivities[0])); } comboBox->setCurrentIndex(pos); } void ActivityCmbBoxDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const { QComboBox *comboBox = static_cast(editor); QStringList assignedActivities = index.model()->data(index, Qt::UserRole).toStringList(); QString selectedActivity = comboBox->currentData().toString(); if (assignedActivities.contains(selectedActivity)) { assignedActivities.removeAll(selectedActivity); } else { assignedActivities.append(selectedActivity); } model->setData(index, assignedActivities, Qt::UserRole); } void ActivityCmbBoxDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const { editor->setGeometry(option.rect); } void ActivityCmbBoxDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const { QStyleOptionViewItem myOptions = option; painter->save(); QStringList assignedActivities = index.model()->data(index, Qt::UserRole).toStringList(); if (assignedActivities.count() > 0) { myOptions.text = assignedActivitiesText(index); QTextDocument doc; QString css; + QString activitiesText = myOptions.text; QBrush nBrush; if ((option.state & QStyle::State_Active) && (option.state & QStyle::State_Selected)) { nBrush = option.palette.brush(QPalette::Active, QPalette::HighlightedText); } else { nBrush = option.palette.brush(QPalette::Inactive, QPalette::Text); } css = QString("body { color : %1; }").arg(nBrush.color().name()); + doc.setDefaultStyleSheet(css); doc.setHtml("" + myOptions.text + ""); myOptions.text = ""; myOptions.widget->style()->drawControl(QStyle::CE_ItemViewItem, &myOptions, painter); //we need an offset to be in the same vertical center of TextEdit - int offsetY = 1 + (myOptions.rect.height() - doc.size().height()) / 2; + int offsetY = ((myOptions.rect.height() - doc.size().height()) / 2); - painter->translate(myOptions.rect.left(), myOptions.rect.top() + offsetY); - QRect clip(0, 0, myOptions.rect.width(), myOptions.rect.height()); + if ((qApp->layoutDirection() == Qt::RightToLeft) && !activitiesText.isEmpty()) { + int textWidth = doc.size().width(); + + painter->translate(qMax(myOptions.rect.left(), myOptions.rect.right() - textWidth), myOptions.rect.top() + offsetY + 1); + } else { + painter->translate(myOptions.rect.left(), myOptions.rect.top() + offsetY + 1); + } + QRect clip(0, 0, myOptions.rect.width(), myOptions.rect.height()); doc.drawContents(painter, clip); } else { QApplication::style()->drawControl(QStyle::CE_ItemViewItem, &myOptions, painter); } painter->restore(); } QString ActivityCmbBoxDelegate::assignedActivitiesText(const QModelIndex &index) const { QStringList assignedActivities = index.model()->data(index, Qt::UserRole).toStringList(); QString finalText; if (assignedActivities.count() > 0) { for (int i = 0; i < assignedActivities.count(); ++i) { KActivities::Info info(assignedActivities[i]); if (info.state() != KActivities::Info::Invalid) { if (i > 0) { finalText += ", "; } bool isActive{false}; if ((info.state() == KActivities::Info::Running) || (info.state() == KActivities::Info::Starting)) { isActive = true; } finalText += isActive ? "" + info.name() + "" : info.name(); } } } return finalText; } diff --git a/app/layoutsDelegates/layoutnamedelegate.cpp b/app/layoutsDelegates/layoutnamedelegate.cpp index b0c60a36..e9ca9ec3 100644 --- a/app/layoutsDelegates/layoutnamedelegate.cpp +++ b/app/layoutsDelegates/layoutnamedelegate.cpp @@ -1,141 +1,147 @@ /* * Copyright 2017-2018 Michail Vourlakos * * This file is part of Latte-Dock * * Latte-Dock is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * Latte-Dock is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "layoutnamedelegate.h" #include #include #include #include #include #include #include #include const int HIDDENTEXTCOLUMN = 1; LayoutNameDelegate::LayoutNameDelegate(QObject *parent) : QStyledItemDelegate(parent) { } void LayoutNameDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const { bool isLocked = index.data(Qt::UserRole).toBool(); if (isLocked) { QStandardItemModel *model = (QStandardItemModel *) index.model(); QString nameText = index.data(Qt::DisplayRole).toString(); //! font metrics QFontMetrics fm(option.font); int textWidth = fm.width(nameText); int thick = option.rect.height(); - int startWidth = qBound(0, option.rect.width() - textWidth - thick , thick); + int startWidth = (qApp->layoutDirection() == Qt::RightToLeft) ? thick : qBound(0, option.rect.width() - textWidth - thick , thick); + int endWidth = (qApp->layoutDirection() == Qt::RightToLeft) ? qBound(0, option.rect.width() - textWidth - thick , thick) : thick; QRect destinationS(option.rect.x(), option.rect.y(), startWidth, thick); - QRect destinationE(option.rect.x() + option.rect.width() - thick, option.rect.y(), thick, thick); + QRect destinationE(option.rect.x() + option.rect.width() - thick, option.rect.y(), endWidth, thick); QStyleOptionViewItem myOptionS = option; QStyleOptionViewItem myOptionE = option; QStyleOptionViewItem myOptionMain = option; myOptionS.rect = destinationS; myOptionE.rect = destinationE; myOptionMain.rect.setX(option.rect.x() + startWidth); - myOptionMain.rect.setWidth(option.rect.width() - startWidth - thick); + myOptionMain.rect.setWidth(option.rect.width() - startWidth - endWidth); QStyledItemDelegate::paint(painter, myOptionMain, index); //! draw background at edges QStyledItemDelegate::paint(painter, myOptionS, model->index(index.row(), HIDDENTEXTCOLUMN)); //! Lock Icon 1: Unicode character attempt /*QString s = QChar(0x2318); QString s = QChar(0x2757); myOptionE.text = s;*/ QStyledItemDelegate::paint(painter, myOptionE, model->index(index.row(), HIDDENTEXTCOLUMN)); //! Lock Icon 2: QIcon attempt that doesnt change color QIcon lockIcon = QIcon::fromTheme("object-locked"); - painter->drawPixmap(destinationE, lockIcon.pixmap(thick, thick)); + + if (qApp->layoutDirection() == Qt::RightToLeft) { + painter->drawPixmap(destinationS, lockIcon.pixmap(thick, thick)); + } else { + painter->drawPixmap(destinationE, lockIcon.pixmap(thick, thick)); + } //! Lock Icon 3: QIcon and change colors attempt /*QIcon lockIcon = QIcon::fromTheme("object-locked"); QPixmap origPixmap = lockIcon.pixmap(thick, thick); QPixmap lockPixmap = origPixmap; QBrush nBrush; if ((option.state & QStyle::State_Active) && (option.state & QStyle::State_Selected)) { nBrush = option.palette.brush(QPalette::Active, QPalette::HighlightedText); } else { nBrush = option.palette.brush(QPalette::Inactive, QPalette::Text); } lockPixmap.fill(nBrush.color()); lockPixmap.setMask(origPixmap.createMaskFromColor(Qt::transparent)); painter->drawPixmap(destinationE, lockPixmap);*/ //! Lock Icon 4: Plasma::Svg and change color group attempt /*Plasma::Svg svgIcon; svgIcon.setStatus(Plasma::Svg::Normal); svgIcon.setUsingRenderingCache(false); svgIcon.setDevicePixelRatio(qApp->devicePixelRatio()); //! find path //try to load from iconloader an svg with Plasma::Svg const auto *iconTheme = KIconLoader::global()->theme(); QString iconPath; if (iconTheme) { iconPath = iconTheme->iconPath("object-locked" + QLatin1String(".svg") , thick , KIconLoader::MatchBest); if (iconPath.isEmpty()) { iconPath = iconTheme->iconPath("object-locked" + QLatin1String(".svgz") , thick , KIconLoader::MatchBest); } } else { qWarning() << "KIconLoader has no theme set"; } if (!iconPath.isEmpty()) { svgIcon.setImagePath(iconPath); if ((option.state & QStyle::State_Active) && (option.state & QStyle::State_Selected)) { svgIcon.setColorGroup(Plasma::Theme::ComplementaryColorGroup); } else { svgIcon.setColorGroup(Plasma::Theme::NormalColorGroup); } svgIcon.resize(thick, thick); } painter->drawPixmap(destinationE, svgIcon.pixmap());*/ return; } QStyledItemDelegate::paint(painter, option, index); } diff --git a/app/settingsdialog.cpp b/app/settingsdialog.cpp index 88d48d7e..bd7a950a 100644 --- a/app/settingsdialog.cpp +++ b/app/settingsdialog.cpp @@ -1,1511 +1,1518 @@ /* * Copyright 2017 Smith AR * Michail Vourlakos * * This file is part of Latte-Dock * * Latte-Dock is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * Latte-Dock is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "settingsdialog.h" #include "dockcorona.h" #include "layout.h" #include "layoutmanager.h" #include "importer.h" #include "universalsettings.h" #include "ui_settingsdialog.h" #include "../liblattedock/dock.h" #include "layoutsDelegates/checkboxdelegate.h" #include "layoutsDelegates/colorcmbboxdelegate.h" #include "layoutsDelegates/activitycmbboxdelegate.h" #include "layoutsDelegates/layoutnamedelegate.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Latte { const int IDCOLUMN = 0; const int HIDDENTEXTCOLUMN = 1; const int COLORCOLUMN = 2; const int NAMECOLUMN = 3; const int MENUCOLUMN = 4; const int ACTIVITYCOLUMN = 5; const int SCREENTRACKERDEFAULTVALUE = 2500; const QChar CheckMark{0x2714}; SettingsDialog::SettingsDialog(QWidget *parent, DockCorona *corona) : QDialog(parent), ui(new Ui::SettingsDialog), m_corona(corona) { ui->setupUi(this); setAttribute(Qt::WA_DeleteOnClose, true); setWindowFlags(windowFlags() | Qt::WindowStaysOnTopHint); resize(m_corona->universalSettings()->layoutsWindowSize()); connect(ui->buttonBox->button(QDialogButtonBox::Apply), &QPushButton::clicked , this, &SettingsDialog::apply); connect(ui->buttonBox->button(QDialogButtonBox::RestoreDefaults), &QPushButton::clicked , this, &SettingsDialog::restoreDefaults); m_model = new QStandardItemModel(m_corona->layoutManager()->layouts().count(), 6, this); ui->layoutsView->setModel(m_model); ui->layoutsView->horizontalHeader()->setStretchLastSection(true); ui->layoutsView->verticalHeader()->setVisible(false); connect(m_corona->layoutManager(), &LayoutManager::currentLayoutNameChanged, this, &SettingsDialog::layoutsChanged); connect(m_corona->layoutManager(), &LayoutManager::activeLayoutsChanged, this, &SettingsDialog::layoutsChanged); QString iconsPath(m_corona->kPackage().path() + "../../plasmoids/org.kde.latte.containment/contents/icons/"); //!find the available colors QDir layoutDir(iconsPath); QStringList filter; filter.append(QString("*print.jpg")); QStringList files = layoutDir.entryList(filter, QDir::Files | QDir::NoSymLinks); QStringList colors; foreach (auto file, files) { int colorEnd = file.lastIndexOf("print.jpg"); QString color = file.remove(colorEnd, 9); colors.append(color); } ui->layoutsView->setItemDelegateForColumn(NAMECOLUMN, new LayoutNameDelegate(this)); ui->layoutsView->setItemDelegateForColumn(COLORCOLUMN, new ColorCmbBoxDelegate(this, iconsPath, colors)); ui->layoutsView->setItemDelegateForColumn(MENUCOLUMN, new CheckBoxDelegate(this)); ui->layoutsView->setItemDelegateForColumn(ACTIVITYCOLUMN, new ActivityCmbBoxDelegate(this)); m_inMemoryButtons = new QButtonGroup(this); m_inMemoryButtons->addButton(ui->singleToolBtn, Latte::Dock::SingleLayout); m_inMemoryButtons->addButton(ui->multipleToolBtn, Latte::Dock::MultipleLayouts); m_inMemoryButtons->setExclusive(true); if (KWindowSystem::isPlatformWayland()) { m_inMemoryButtons->button(Latte::Dock::MultipleLayouts)->setEnabled(false); } m_mouseSensitivityButtons = new QButtonGroup(this); m_mouseSensitivityButtons->addButton(ui->lowSensitivityBtn, Latte::Dock::LowSensitivity); m_mouseSensitivityButtons->addButton(ui->mediumSensitivityBtn, Latte::Dock::MediumSensitivity); m_mouseSensitivityButtons->addButton(ui->highSensitivityBtn, Latte::Dock::HighSensitivity); m_mouseSensitivityButtons->setExclusive(true); ui->screenTrackerSpinBox->setValue(m_corona->universalSettings()->screenTrackerInterval()); //! About Menu QMenuBar *menuBar = new QMenuBar(this); // QMenuBar *rightAlignedMenuBar = new QMenuBar(menuBar); layout()->setMenuBar(menuBar); //menuBar->setCornerWidget(rightAlignedMenuBar); QMenu *fileMenu = new QMenu(i18n("File"), menuBar); menuBar->addMenu(fileMenu); QMenu *helpMenu = new QMenu(i18n("Help"), menuBar); //rightAlignedMenuBar->addMenu(helpMenu); menuBar->addMenu(helpMenu); QAction *quitAction = fileMenu->addAction(i18n("Quit Latte")); quitAction->setIcon(QIcon::fromTheme("application-exit")); quitAction->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_Q)); QAction *aboutAction = helpMenu->addAction(i18n("About Latte")); aboutAction->setIcon(QIcon::fromTheme("latte-dock")); + //! RTL support for labels in preferences + if (qApp->layoutDirection() == Qt::RightToLeft) { + ui->behaviorLbl->setAlignment(Qt::AlignRight | Qt::AlignTop); + ui->mouseSensetivityLbl->setAlignment(Qt::AlignRight | Qt::AlignTop); + ui->delayLbl->setAlignment(Qt::AlignRight | Qt::AlignTop); + } + loadSettings(); //! SIGNALS connect(m_model, &QStandardItemModel::itemChanged, this, &SettingsDialog::itemChanged); connect(ui->layoutsView->selectionModel(), &QItemSelectionModel::currentRowChanged, this, [&]() { updatePerLayoutButtonsState(); updateApplyButtonsState(); }); connect(m_inMemoryButtons, static_cast(&QButtonGroup::buttonToggled), [ = ](int id, bool checked) { updateApplyButtonsState(); }); connect(m_mouseSensitivityButtons, static_cast(&QButtonGroup::buttonToggled), [ = ](int id, bool checked) { updateApplyButtonsState(); }); connect(ui->screenTrackerSpinBox, QOverload::of(&QSpinBox::valueChanged), [ = ](int i) { updateApplyButtonsState(); }); connect(ui->autostartChkBox, &QCheckBox::stateChanged, this, &SettingsDialog::updateApplyButtonsState); connect(ui->infoWindowChkBox, &QCheckBox::stateChanged, this, &SettingsDialog::updateApplyButtonsState); connect(ui->tabWidget, &QTabWidget::currentChanged, this, &SettingsDialog::updateApplyButtonsState); connect(aboutAction, &QAction::triggered, m_corona, &DockCorona::aboutApplication); connect(quitAction, &QAction::triggered, m_corona, &DockCorona::closeApplication); //! update all layouts view when runningActivities changed. This way we update immediately //! the running Activities in Activities checkboxes which are shown as bold connect(m_corona->activitiesConsumer(), &KActivities::Consumer::runningActivitiesChanged, this, [&]() { ui->layoutsView->update(); }); blockDeleteOnActivityStopped(); } SettingsDialog::~SettingsDialog() { qDebug() << Q_FUNC_INFO; qDeleteAll(m_layouts); if (m_model) { delete m_model; } if (m_corona && m_corona->universalSettings()) { m_corona->universalSettings()->setLayoutsWindowSize(size()); QStringList columnWidths; columnWidths << QString::number(ui->layoutsView->columnWidth(COLORCOLUMN)); columnWidths << QString::number(ui->layoutsView->columnWidth(NAMECOLUMN)); columnWidths << QString::number(ui->layoutsView->columnWidth(MENUCOLUMN)); m_corona->universalSettings()->setLayoutsColumnWidths(columnWidths); } m_inMemoryButtons->deleteLater(); m_mouseSensitivityButtons->deleteLater(); foreach (auto tempDir, m_tempDirectories) { QDir tDir(tempDir); if (tDir.exists() && tempDir.startsWith("/tmp/")) { tDir.removeRecursively(); } } } void SettingsDialog::blockDeleteOnActivityStopped() { connect(m_corona->activitiesConsumer(), &KActivities::Consumer::runningActivitiesChanged, this, [&]() { m_blockDeleteOnReject = true; m_activityClosedTimer.start(); }); m_activityClosedTimer.setSingleShot(true); m_activityClosedTimer.setInterval(500); connect(&m_activityClosedTimer, &QTimer::timeout, this, [&]() { m_blockDeleteOnReject = false; }); } QStringList SettingsDialog::activities() { return m_corona->layoutManager()->activities(); } QStringList SettingsDialog::availableActivities() { return m_availableActivities; } void SettingsDialog::setCurrentPage(Dock::LatteConfigPage page) { if (page == Dock::LayoutPage) { ui->tabWidget->setCurrentIndex(0); } else if (page == Dock::PreferencesPage) { ui->tabWidget->setCurrentIndex(1); } } void SettingsDialog::on_newButton_clicked() { qDebug() << Q_FUNC_INFO; //! find Default preset path foreach (auto preset, m_corona->layoutManager()->presetsPaths()) { QString presetName = Layout::layoutName(preset); if (presetName == "Default") { QByteArray presetNameChars = presetName.toUtf8(); const char *prset_str = presetNameChars.data(); presetName = uniqueLayoutName(i18n(prset_str)); addLayoutForFile(preset, presetName, true, false); break; } } } void SettingsDialog::on_copyButton_clicked() { qDebug() << Q_FUNC_INFO; int row = ui->layoutsView->currentIndex().row(); if (row < 0) { return; } //! Update original layout before copying if this layout is active if (m_corona->layoutManager()->memoryUsage() == Dock::MultipleLayouts) { QString lName = (m_model->data(m_model->index(row, NAMECOLUMN), Qt::DisplayRole)).toString(); if (Importer::layoutExists(lName)) { Layout *layout = m_corona->layoutManager()->activeLayout(lName); if (layout && layout->isOriginalLayout()) { layout->syncToLayoutFile(); } } } QString tempDir = uniqueTempDirectory(); QString id = m_model->data(m_model->index(row, IDCOLUMN), Qt::DisplayRole).toString(); QString color = m_model->data(m_model->index(row, COLORCOLUMN), Qt::BackgroundRole).toString(); QString textColor = m_model->data(m_model->index(row, COLORCOLUMN), Qt::UserRole).toString(); QString layoutName = uniqueLayoutName(m_model->data(m_model->index(row, NAMECOLUMN), Qt::DisplayRole).toString()); bool menu = m_model->data(m_model->index(row, MENUCOLUMN), Qt::DisplayRole).toString() == CheckMark; QString copiedId = tempDir + "/" + layoutName + ".layout.latte"; QFile(id).copy(copiedId); QFileInfo newFileInfo(copiedId); if (newFileInfo.exists() && !newFileInfo.isWritable()) { QFile(copiedId).setPermissions(QFileDevice::ReadUser | QFileDevice::WriteUser | QFileDevice::ReadGroup | QFileDevice::ReadOther); } Layout *settings = new Layout(this, copiedId); m_layouts[copiedId] = settings; insertLayoutInfoAtRow(row + 1, copiedId, color, textColor, layoutName, menu, QStringList(), false); ui->layoutsView->selectRow(row + 1); } void SettingsDialog::on_downloadButton_clicked() { qDebug() << Q_FUNC_INFO; KNS3::DownloadDialog dialog(QStringLiteral("latte-layouts.knsrc"), this); dialog.resize(m_corona->universalSettings()->downloadWindowSize()); dialog.exec(); bool layoutAdded{false}; if (!dialog.changedEntries().isEmpty() || !dialog.installedEntries().isEmpty()) { foreach (auto entry, dialog.installedEntries()) { foreach (auto entryFile, entry.installedFiles()) { Importer::LatteFileVersion version = Importer::fileVersion(entryFile); if (version == Importer::LayoutVersion2) { layoutAdded = true; addLayoutForFile(entryFile); break; } } } } m_corona->universalSettings()->setDownloadWindowSize(dialog.size()); if (layoutAdded) { apply(); } } void SettingsDialog::on_removeButton_clicked() { qDebug() << Q_FUNC_INFO; int row = ui->layoutsView->currentIndex().row(); if (row < 0) { return; } QString layoutName = m_model->data(m_model->index(row, NAMECOLUMN), Qt::DisplayRole).toString(); if (m_corona->layoutManager()->activeLayout(layoutName)) { return; } m_model->removeRow(row); updateApplyButtonsState(); row = qMax(row - 1, 0); ui->layoutsView->selectRow(row); } void SettingsDialog::on_lockedButton_clicked() { qDebug() << Q_FUNC_INFO; int row = ui->layoutsView->currentIndex().row(); if (row < 0) { return; } bool lockedModel = m_model->data(m_model->index(row, NAMECOLUMN), Qt::UserRole).toBool(); m_model->setData(m_model->index(row, NAMECOLUMN), QVariant(!lockedModel), Qt::UserRole); updatePerLayoutButtonsState(); updateApplyButtonsState(); } void SettingsDialog::on_importButton_clicked() { qDebug() << Q_FUNC_INFO; QFileDialog *fileDialog = new QFileDialog(this, i18nc("import layout/configuration", "Import Layout/Configuration") , QDir::homePath() , QStringLiteral("layout.latte")); fileDialog->setFileMode(QFileDialog::AnyFile); fileDialog->setAcceptMode(QFileDialog::AcceptOpen); fileDialog->setDefaultSuffix("layout.latte"); QStringList filters; filters << QString(i18nc("import latte layout", "Latte Dock Layout file v0.2") + "(*.layout.latte)") << QString(i18nc("import latte layouts/configuration", "Latte Dock Full Configuration file (v0.1, v0.2)") + "(*.latterc)"); fileDialog->setNameFilters(filters); connect(fileDialog, &QFileDialog::finished , fileDialog, &QFileDialog::deleteLater); connect(fileDialog, &QFileDialog::fileSelected , this, [&](const QString & file) { Importer::LatteFileVersion version = Importer::fileVersion(file); qDebug() << "VERSION :::: " << version; if (version == Importer::LayoutVersion2) { addLayoutForFile(file); } else if (version == Importer::ConfigVersion1) { auto msg = new QMessageBox(this); msg->setIcon(QMessageBox::Warning); msg->setWindowTitle(i18n("Import: Configuration file version v0.1")); msg->setText( i18n("You are going to import an old version v0.1 configuration file.
Be careful, importing the entire configuration will erase all your current configuration!!!

Alternative, you can import safely from this file
only the contained layouts...
")); msg->setStandardButtons(QMessageBox::Cancel); QPushButton *fullBtn = new QPushButton(msg); QPushButton *layoutsBtn = new QPushButton(msg); fullBtn->setText(i18nc("import full configuration", "Full Configuration")); fullBtn->setIcon(QIcon::fromTheme("settings")); layoutsBtn->setText(i18nc("import only the layouts", "Only Layouts")); layoutsBtn->setIcon(QIcon::fromTheme("user-identity")); msg->addButton(fullBtn, QMessageBox::AcceptRole); msg->addButton(layoutsBtn, QMessageBox::AcceptRole); msg->setDefaultButton(layoutsBtn); connect(msg, &QMessageBox::finished, msg, &QMessageBox::deleteLater); msg->open(); connect(layoutsBtn, &QPushButton::clicked , this, [ &, file](bool check) { importLayoutsFromV1ConfigFile(file); }); connect(fullBtn, &QPushButton::clicked , this, [ &, file](bool check) { //!NOTE: Restart latte for import the new configuration QProcess::startDetached(qGuiApp->applicationFilePath() + " --import-full \"" + file + "\""); qGuiApp->exit(); }); } else if (version == Importer::ConfigVersion2) { auto msg = new QMessageBox(this); msg->setIcon(QMessageBox::Warning); msg->setWindowTitle(i18n("Import: Configuration file version v0.2")); msg->setText( i18n("You are going to import a v0.2 configuration file.
Be careful, importing will erase all your current configuration!!!

Would you like to proceed?")); msg->setStandardButtons(QMessageBox::Yes | QMessageBox::No); msg->setDefaultButton(QMessageBox::No); connect(msg, &QMessageBox::finished, this, [ &, msg, file](int result) { if (result == QMessageBox::Yes) { //!NOTE: Restart latte for import the new configuration msg->deleteLater(); QProcess::startDetached(qGuiApp->applicationFilePath() + " --import-full \"" + file + "\""); qGuiApp->exit(); } }); msg->open(); } }); fileDialog->open(); } bool SettingsDialog::importLayoutsFromV1ConfigFile(QString file) { KTar archive(file, QStringLiteral("application/x-tar")); archive.open(QIODevice::ReadOnly); //! if the file isnt a tar archive if (archive.isOpen()) { QDir tempDir{uniqueTempDirectory()}; const auto archiveRootDir = archive.directory(); foreach (auto &name, archiveRootDir->entries()) { auto fileEntry = archiveRootDir->file(name); fileEntry->copyTo(tempDir.absolutePath()); } QString name = Importer::nameOfConfigFile(file); QString applets(tempDir.absolutePath() + "/" + "lattedock-appletsrc"); if (QFile(applets).exists()) { if (m_corona->layoutManager()->importer()->importOldLayout(applets, name, false, tempDir.absolutePath())) { addLayoutForFile(tempDir.absolutePath() + "/" + name + ".layout.latte", name, false); } QString alternativeName = name + "-" + i18nc("layout", "Alternative"); if (m_corona->layoutManager()->importer()->importOldLayout(applets, alternativeName, false, tempDir.absolutePath())) { addLayoutForFile(tempDir.absolutePath() + "/" + alternativeName + ".layout.latte", alternativeName, false); } } return true; } return false; } void SettingsDialog::on_exportButton_clicked() { int row = ui->layoutsView->currentIndex().row(); if (row < 0) { return; } QString layoutExported = m_model->data(m_model->index(row, IDCOLUMN), Qt::DisplayRole).toString(); //! Update ALL active original layouts before exporting, //! this is needed because the export method can export also the full configuration qDebug() << Q_FUNC_INFO; m_corona->layoutManager()->syncActiveLayoutsToOriginalFiles(); QFileDialog *fileDialog = new QFileDialog(this, i18nc("export layout/configuration", "Export Layout/Configuration") , QDir::homePath() , QStringLiteral("layout.latte")); fileDialog->setFileMode(QFileDialog::AnyFile); fileDialog->setAcceptMode(QFileDialog::AcceptSave); fileDialog->setDefaultSuffix("layout.latte"); QStringList filters; QString filter1(i18nc("export layout", "Latte Dock Layout file v0.2") + "(*.layout.latte)"); QString filter2(i18nc("export full configuration", "Latte Dock Full Configuration file v0.2") + "(*.latterc)"); filters << filter1 << filter2; fileDialog->setNameFilters(filters); connect(fileDialog, &QFileDialog::finished , fileDialog, &QFileDialog::deleteLater); connect(fileDialog, &QFileDialog::fileSelected , this, [ &, layoutExported](const QString & file) { auto showNotificationError = []() { auto notification = new KNotification("export-fail", KNotification::CloseOnTimeout); notification->setText(i18nc("export layout", "Failed to export layout")); notification->sendEvent(); }; if (QFile::exists(file) && !QFile::remove(file)) { showNotificationError(); return; } if (file.endsWith(".layout.latte")) { if (!QFile(layoutExported).copy(file)) { showNotificationError(); return; } QFileInfo newFileInfo(file); if (newFileInfo.exists() && !newFileInfo.isWritable()) { QFile(file).setPermissions(QFileDevice::ReadUser | QFileDevice::WriteUser | QFileDevice::ReadGroup | QFileDevice::ReadOther); } Layout layoutS(this, file); layoutS.setActivities(QStringList()); layoutS.clearLastUsedActivity(); //NOTE: The pointer is automatically deleted when the event is closed auto notification = new KNotification("export-done", KNotification::CloseOnTimeout); notification->setActions({i18nc("export layout", "Open location")}); notification->setText(i18nc("export layout", "Layout exported successfully")); connect(notification, &KNotification::action1Activated , this, [file]() { QDesktopServices::openUrl({QFileInfo(file).canonicalPath()}); }); notification->sendEvent(); } else if (file.endsWith(".latterc")) { auto showNotificationError = []() { auto notification = new KNotification("export-fail", KNotification::CloseOnTimeout); notification->setText(i18nc("import/export config", "Failed to export configuration")); notification->sendEvent(); }; if (m_corona->layoutManager()->importer()->exportFullConfiguration(file)) { auto notification = new KNotification("export-done", KNotification::CloseOnTimeout); notification->setActions({i18nc("import/export config", "Open location")}); notification->setText(i18nc("import/export config", "Full Configuration exported successfully")); connect(notification, &KNotification::action1Activated , this, [file]() { QDesktopServices::openUrl({QFileInfo(file).canonicalPath()}); }); notification->sendEvent(); } else { showNotificationError(); } } }); fileDialog->open(); } void SettingsDialog::requestImagesDialog(int row) { QStringList mimeTypeFilters; mimeTypeFilters << "image/jpeg" // will show "JPEG image (*.jpeg *.jpg) << "image/png"; // will show "PNG image (*.png)" QFileDialog dialog(this); dialog.setMimeTypeFilters(mimeTypeFilters); QString background = m_model->data(m_model->index(row, COLORCOLUMN), Qt::BackgroundRole).toString(); if (background.startsWith("/")) { dialog.selectFile(background); } if (dialog.exec()) { QStringList files = dialog.selectedFiles(); if (files.count() > 0) { m_model->setData(m_model->index(row, COLORCOLUMN), files[0], Qt::BackgroundRole); } } } void SettingsDialog::requestColorsDialog(int row) { QColorDialog dialog(this); QString textColor = m_model->data(m_model->index(row, COLORCOLUMN), Qt::UserRole).toString(); dialog.setCurrentColor(QColor(textColor)); if (dialog.exec()) { qDebug() << dialog.selectedColor().name(); m_model->setData(m_model->index(row, COLORCOLUMN), dialog.selectedColor().name(), Qt::UserRole); } } void SettingsDialog::accept() { qDebug() << Q_FUNC_INFO; if (saveAllChanges()) { deleteLater(); } } void SettingsDialog::reject() { qDebug() << Q_FUNC_INFO; if (!m_blockDeleteOnReject) { deleteLater(); } } void SettingsDialog::apply() { qDebug() << Q_FUNC_INFO; saveAllChanges(); o_settings = currentSettings(); o_settingsLayouts = currentLayoutsSettings(); updateApplyButtonsState(); updatePerLayoutButtonsState(); } void SettingsDialog::restoreDefaults() { qDebug() << Q_FUNC_INFO; if (ui->tabWidget->currentIndex() == 0) { //! Default layouts missing from layouts list foreach (auto preset, m_corona->layoutManager()->presetsPaths()) { QString presetName = Layout::layoutName(preset); QByteArray presetNameChars = presetName.toUtf8(); const char *prset_str = presetNameChars.data(); presetName = i18n(prset_str); if (!nameExistsInModel(presetName)) { addLayoutForFile(preset, presetName); } } } else if (ui->tabWidget->currentIndex() == 1) { //! Defaults for general Latte settings ui->autostartChkBox->setChecked(true); ui->infoWindowChkBox->setChecked(true); ui->highSensitivityBtn->setChecked(true); ui->screenTrackerSpinBox->setValue(SCREENTRACKERDEFAULTVALUE); } } void SettingsDialog::addLayoutForFile(QString file, QString layoutName, bool newTempDirectory, bool showNotification) { if (layoutName.isEmpty()) { layoutName = Layout::layoutName(file); } QString copiedId; if (newTempDirectory) { QString tempDir = uniqueTempDirectory(); copiedId = tempDir + "/" + layoutName + ".layout.latte"; QFile(file).copy(copiedId); } else { copiedId = file; } QFileInfo newFileInfo(copiedId); if (newFileInfo.exists() && !newFileInfo.isWritable()) { QFile(copiedId).setPermissions(QFileDevice::ReadUser | QFileDevice::WriteUser | QFileDevice::ReadGroup | QFileDevice::ReadOther); } if (m_layouts.contains(copiedId)) { Layout *oldSettings = m_layouts.take(copiedId); delete oldSettings; } Layout *settings = new Layout(this, copiedId); m_layouts[copiedId] = settings; QString id = copiedId; QString color = settings->color(); QString textColor = settings->textColor(); QString background = settings->background(); bool menu = settings->showInMenu(); bool locked = !settings->isWritable(); layoutName = uniqueLayoutName(layoutName); int row = ascendingRowFor(layoutName); if (background.isEmpty()) { insertLayoutInfoAtRow(row, copiedId, color, QString(), layoutName, menu, QStringList(), locked); } else { insertLayoutInfoAtRow(row, copiedId, background, textColor, layoutName, menu, QStringList(), locked); } ui->layoutsView->selectRow(row); if (showNotification) { //NOTE: The pointer is automatically deleted when the event is closed auto notification = new KNotification("import-done", KNotification::CloseOnTimeout); notification->setText(i18nc("import-done", "Layout: %0 imported successfully
").arg(layoutName)); notification->sendEvent(); } } void SettingsDialog::loadSettings() { m_initLayoutPaths.clear(); m_model->clear(); int i = 0; QStringList brokenLayouts; if (m_corona->layoutManager()->memoryUsage() == Dock::MultipleLayouts) { m_corona->layoutManager()->syncActiveLayoutsToOriginalFiles(); } foreach (auto layout, m_corona->layoutManager()->layouts()) { QString layoutPath = QDir::homePath() + "/.config/latte/" + layout + ".layout.latte"; m_initLayoutPaths.append(layoutPath); Layout *layoutSets = new Layout(this, layoutPath); m_layouts[layoutPath] = layoutSets; QString background = layoutSets->background(); if (background.isEmpty()) { insertLayoutInfoAtRow(i, layoutPath, layoutSets->color(), QString(), layoutSets->name(), layoutSets->showInMenu(), layoutSets->activities(), !layoutSets->isWritable()); } else { insertLayoutInfoAtRow(i, layoutPath, background, layoutSets->textColor(), layoutSets->name(), layoutSets->showInMenu(), layoutSets->activities(), !layoutSets->isWritable()); } qDebug() << "counter:" << i << " total:" << m_model->rowCount(); i++; if (layoutSets->name() == m_corona->layoutManager()->currentLayoutName()) { ui->layoutsView->selectRow(i - 1); } Layout *activeLayout = m_corona->layoutManager()->activeLayout(layoutSets->name()); if ((activeLayout && activeLayout->layoutIsBroken()) || (!activeLayout && layoutSets->layoutIsBroken())) { brokenLayouts.append(layoutSets->name()); } } recalculateAvailableActivities(); m_model->setHorizontalHeaderItem(IDCOLUMN, new QStandardItem(QString("#path"))); m_model->setHorizontalHeaderItem(COLORCOLUMN, new QStandardItem(QString(i18n("Background")))); m_model->setHorizontalHeaderItem(NAMECOLUMN, new QStandardItem(QString(i18n("Name")))); m_model->setHorizontalHeaderItem(MENUCOLUMN, new QStandardItem(QString(i18n("In Menu")))); m_model->setHorizontalHeaderItem(ACTIVITYCOLUMN, new QStandardItem(QString(i18n("Activities")))); //! this line should be commented for debugging layouts window functionality ui->layoutsView->setColumnHidden(IDCOLUMN, true); ui->layoutsView->setColumnHidden(HIDDENTEXTCOLUMN, true); ui->layoutsView->resizeColumnsToContents(); QStringList columnWidths = m_corona->universalSettings()->layoutsColumnWidths(); if (!columnWidths.isEmpty() && columnWidths.count() == 3) { ui->layoutsView->setColumnWidth(COLORCOLUMN, columnWidths[0].toInt()); ui->layoutsView->setColumnWidth(NAMECOLUMN, columnWidths[1].toInt()); ui->layoutsView->setColumnWidth(MENUCOLUMN, columnWidths[2].toInt()); } if (m_corona->layoutManager()->memoryUsage() == Dock::SingleLayout) { ui->singleToolBtn->setChecked(true); } else if (m_corona->layoutManager()->memoryUsage() == Dock::MultipleLayouts) { ui->multipleToolBtn->setChecked(true); } updatePerLayoutButtonsState(); ui->autostartChkBox->setChecked(m_corona->universalSettings()->autostart()); ui->infoWindowChkBox->setChecked(m_corona->universalSettings()->showInfoWindow()); if (m_corona->universalSettings()->mouseSensitivity() == Dock::LowSensitivity) { ui->lowSensitivityBtn->setChecked(true); } else if (m_corona->universalSettings()->mouseSensitivity() == Dock::MediumSensitivity) { ui->mediumSensitivityBtn->setChecked(true); } else if (m_corona->universalSettings()->mouseSensitivity() == Dock::HighSensitivity) { ui->highSensitivityBtn->setChecked(true); } o_settings = currentSettings(); o_settingsLayouts = currentLayoutsSettings(); updateApplyButtonsState(); //! there are broken layouts and the user must be informed! if (brokenLayouts.count() > 0) { auto msg = new QMessageBox(this); msg->setIcon(QMessageBox::Warning); msg->setWindowTitle(i18n("Layout Warning")); msg->setText(i18n("The layout(s) %0 have broken configuration!!! Please remove them to improve the system stability...").arg(brokenLayouts.join(","))); msg->setStandardButtons(QMessageBox::Ok); msg->open(); } } QList SettingsDialog::currentSettings() { QList settings; settings << m_inMemoryButtons->checkedId(); settings << (int)ui->autostartChkBox->isChecked(); settings << (int)ui->infoWindowChkBox->isChecked(); settings << m_mouseSensitivityButtons->checkedId(); settings << ui->screenTrackerSpinBox->value(); settings << m_model->rowCount(); return settings; } QStringList SettingsDialog::currentLayoutsSettings() { QStringList layoutSettings; for (int i = 0; i < m_model->rowCount(); ++i) { QString id = m_model->data(m_model->index(i, IDCOLUMN), Qt::DisplayRole).toString(); QString color = m_model->data(m_model->index(i, COLORCOLUMN), Qt::BackgroundRole).toString(); QString textColor = m_model->data(m_model->index(i, COLORCOLUMN), Qt::UserRole).toString(); QString name = m_model->data(m_model->index(i, NAMECOLUMN), Qt::DisplayRole).toString(); bool locked = m_model->data(m_model->index(i, NAMECOLUMN), Qt::UserRole).toBool(); bool menu = m_model->data(m_model->index(i, MENUCOLUMN), Qt::DisplayRole).toString() == CheckMark; QStringList lActivities = m_model->data(m_model->index(i, ACTIVITYCOLUMN), Qt::UserRole).toStringList(); layoutSettings << id; layoutSettings << color; layoutSettings << textColor; layoutSettings << name; layoutSettings << QString::number((int)locked); layoutSettings << QString::number((int)menu); layoutSettings << lActivities; } return layoutSettings; } void SettingsDialog::insertLayoutInfoAtRow(int row, QString path, QString color, QString textColor, QString name, bool menu, QStringList activities, bool locked) { QStandardItem *pathItem = new QStandardItem(path); QStandardItem *hiddenTextItem = new QStandardItem(); QStandardItem *colorItem = new QStandardItem(); colorItem->setSelectable(false); QStandardItem *nameItem = new QStandardItem(name); nameItem->setTextAlignment(Qt::AlignCenter); QStandardItem *menuItem = new QStandardItem(); menuItem->setEditable(false); menuItem->setSelectable(true); menuItem->setText(menu ? CheckMark : QString()); menuItem->setTextAlignment(Qt::AlignCenter); QStandardItem *activitiesItem = new QStandardItem(activities.join(",")); QList items; items.append(pathItem); items.append(hiddenTextItem); items.append(colorItem); items.append(nameItem); items.append(menuItem); items.append(activitiesItem); if (row > m_model->rowCount() - 1) { m_model->appendRow(items); row = m_model->rowCount() - 1; qDebug() << "append row at:" << row << " rows:" << m_model->rowCount(); } else { m_model->insertRow(row, items); qDebug() << "insert row at:" << row << " rows:" << m_model->rowCount(); } m_model->setData(m_model->index(row, IDCOLUMN), path, Qt::DisplayRole); m_model->setData(m_model->index(row, COLORCOLUMN), color, Qt::BackgroundRole); m_model->setData(m_model->index(row, COLORCOLUMN), textColor, Qt::UserRole); QFont font; if (m_corona->layoutManager()->activeLayout(name)) { font.setBold(true); } else { font.setBold(false); } if (path.startsWith("/tmp/")) { font.setItalic(true); } else { font.setItalic(false); } m_model->setData(m_model->index(row, NAMECOLUMN), QVariant(name), Qt::DisplayRole); m_model->setData(m_model->index(row, NAMECOLUMN), font, Qt::FontRole); m_model->setData(m_model->index(row, NAMECOLUMN), QVariant(locked), Qt::UserRole); m_model->setData(m_model->index(row, ACTIVITYCOLUMN), activities, Qt::UserRole); } void SettingsDialog::on_switchButton_clicked() { if (ui->buttonBox->button(QDialogButtonBox::Apply)->isEnabled()) { //! thus there are changes in the settings QString lName; QStringList lActivities; if (m_inMemoryButtons->checkedId() == Latte::Dock::MultipleLayouts) { lName = m_model->data(m_model->index(ui->layoutsView->currentIndex().row(), NAMECOLUMN), Qt::DisplayRole).toString(); lActivities = m_model->data(m_model->index(ui->layoutsView->currentIndex().row(), ACTIVITYCOLUMN), Qt::UserRole).toStringList(); } apply(); if (!lName.isEmpty() && !lActivities.isEmpty()) { //! an activities-assigned layout is chosen and at the same time we are moving //! to multiple layouts state m_corona->layoutManager()->switchToLayout(lName); } } else { QVariant value = m_model->data(m_model->index(ui->layoutsView->currentIndex().row(), NAMECOLUMN), Qt::DisplayRole); if (value.isValid()) { m_corona->layoutManager()->switchToLayout(value.toString()); } else { qDebug() << "not valid layout"; } } updatePerLayoutButtonsState(); } void SettingsDialog::on_pauseButton_clicked() { ui->pauseButton->setEnabled(false); QString id = m_model->data(m_model->index(ui->layoutsView->currentIndex().row(), IDCOLUMN), Qt::DisplayRole).toString(); Layout *layout = m_layouts[id]; if (layout) { m_corona->layoutManager()->pauseLayout(layout->name()); } } void SettingsDialog::layoutsChanged() { for (int i = 0; i < m_model->rowCount(); ++i) { QModelIndex nameIndex = m_model->index(i, NAMECOLUMN); QVariant value = m_model->data(nameIndex); if (value.isValid()) { QString name = value.toString(); QFont font; if (m_corona->layoutManager()->currentLayoutName() == name) { font.setBold(true); // ui->layoutsView->selectRow(i); } else { Layout *layout = m_corona->layoutManager()->activeLayout(name); if (layout && (m_corona->layoutManager()->memoryUsage() == Dock::MultipleLayouts)) { font.setBold(true); } else { font.setBold(false); } } m_model->setData(nameIndex, font, Qt::FontRole); } } } void SettingsDialog::itemChanged(QStandardItem *item) { updatePerLayoutButtonsState(); if (item->column() == ACTIVITYCOLUMN) { //! recalculate the available activities recalculateAvailableActivities(); } else if (item->column() == NAMECOLUMN) { int currentRow = ui->layoutsView->currentIndex().row(); QString id = m_model->data(m_model->index(currentRow, IDCOLUMN), Qt::DisplayRole).toString(); QString name = m_model->data(m_model->index(currentRow, NAMECOLUMN), Qt::DisplayRole).toString(); QFont font = qvariant_cast(m_model->data(m_model->index(currentRow, NAMECOLUMN), Qt::FontRole)); if (m_layouts[id]->name() != name) { font.setItalic(true); m_model->setData(m_model->index(currentRow, NAMECOLUMN), font, Qt::FontRole); } else { font.setItalic(false); m_model->setData(m_model->index(currentRow, NAMECOLUMN), font, Qt::FontRole); } } updateApplyButtonsState(); } void SettingsDialog::updateApplyButtonsState() { bool changed{false}; //! Ok, Apply Buttons if ((o_settings != currentSettings()) || (o_settingsLayouts != currentLayoutsSettings())) { changed = true; } if (changed) { ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(true); ui->buttonBox->button(QDialogButtonBox::Apply)->setEnabled(true); } else { ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); ui->buttonBox->button(QDialogButtonBox::Apply)->setEnabled(false); } //! RestoreDefaults Button if (ui->tabWidget->currentIndex() == 0) { //! Check Default layouts missing from layouts list bool layoutMissing{false}; foreach (auto preset, m_corona->layoutManager()->presetsPaths()) { QString presetName = Layout::layoutName(preset); QByteArray presetNameChars = presetName.toUtf8(); const char *prset_str = presetNameChars.data(); presetName = i18n(prset_str); if (!nameExistsInModel(presetName)) { layoutMissing = true; break; } } if (layoutMissing) { ui->buttonBox->button(QDialogButtonBox::RestoreDefaults)->setEnabled(true); } else { ui->buttonBox->button(QDialogButtonBox::RestoreDefaults)->setEnabled(false); } } else if (ui->tabWidget->currentIndex() == 1) { //! Defaults for general Latte settings if (!ui->autostartChkBox->isChecked() || !ui->infoWindowChkBox->isChecked() || !ui->highSensitivityBtn->isChecked() || ui->screenTrackerSpinBox->value() != SCREENTRACKERDEFAULTVALUE) { ui->buttonBox->button(QDialogButtonBox::RestoreDefaults)->setEnabled(true); } else { ui->buttonBox->button(QDialogButtonBox::RestoreDefaults)->setEnabled(false); } } } void SettingsDialog::updatePerLayoutButtonsState() { int currentRow = ui->layoutsView->currentIndex().row(); QString id = m_model->data(m_model->index(currentRow, IDCOLUMN), Qt::DisplayRole).toString(); QString nameInModel = m_model->data(m_model->index(currentRow, NAMECOLUMN), Qt::DisplayRole).toString(); QString originalName = m_layouts.contains(id) ? m_layouts[id]->name() : ""; bool lockedInModel = m_model->data(m_model->index(currentRow, NAMECOLUMN), Qt::UserRole).toBool(); //! Switch Button if (id.startsWith("/tmp/") || originalName != nameInModel) { ui->switchButton->setEnabled(false); } else { ui->switchButton->setEnabled(true); } //! Pause Button if (m_corona->layoutManager()->memoryUsage() == Dock::SingleLayout) { ui->pauseButton->setVisible(false); } else if (m_corona->layoutManager()->memoryUsage() == Dock::MultipleLayouts) { ui->pauseButton->setVisible(true); QStringList lActivities = m_model->data(m_model->index(currentRow, ACTIVITYCOLUMN), Qt::UserRole).toStringList(); Layout *layout = m_layouts[id]; if (!lActivities.isEmpty() && layout && m_corona->layoutManager()->activeLayout(layout->name())) { ui->pauseButton->setEnabled(true); } else { ui->pauseButton->setEnabled(false); } } //! Remove Layout Button if (originalName != nameInModel || (originalName == m_corona->layoutManager()->currentLayoutName()) || (m_corona->layoutManager()->activeLayout(originalName)) || lockedInModel) { ui->removeButton->setEnabled(false); } else { ui->removeButton->setEnabled(true); } if (lockedInModel) { ui->lockedButton->setChecked(true); } else { ui->lockedButton->setChecked(false); } } void SettingsDialog::recalculateAvailableActivities() { QStringList tempActivities = m_corona->layoutManager()->activities(); for (int i = 0; i < m_model->rowCount(); ++i) { QStringList assigned = m_model->data(m_model->index(i, ACTIVITYCOLUMN), Qt::UserRole).toStringList(); foreach (auto activity, assigned) { if (tempActivities.contains(activity)) { tempActivities.removeAll(activity); } } } m_availableActivities = tempActivities; } bool SettingsDialog::dataAreAccepted() { for (int i = 0; i < m_model->rowCount(); ++i) { QString layout1 = m_model->data(m_model->index(i, NAMECOLUMN), Qt::DisplayRole).toString(); for (int j = i + 1; j < m_model->rowCount(); ++j) { QString temp = m_model->data(m_model->index(j, NAMECOLUMN), Qt::DisplayRole).toString(); //!same layout name exists again if (layout1 == temp) { auto msg = new QMessageBox(this); msg->setIcon(QMessageBox::Warning); msg->setWindowTitle(i18n("Layout Warning")); msg->setText(i18n("There are layouts with the same name, that is not permitted!!! Please update these names to re-apply the changes...")); msg->setStandardButtons(QMessageBox::Ok); connect(msg, &QMessageBox::finished, this, [ &, i, j](int result) { QItemSelectionModel::SelectionFlags flags = QItemSelectionModel::ClearAndSelect; QModelIndex indexBase = m_model->index(i, NAMECOLUMN); ui->layoutsView->selectionModel()->select(indexBase, flags); QModelIndex indexOccurence = m_model->index(j, NAMECOLUMN); ui->layoutsView->edit(indexOccurence); }); msg->open(); return false; } } } return true; } bool SettingsDialog::saveAllChanges() { if (!dataAreAccepted()) { return false; } //! Update universal settings Latte::Dock::MouseSensitivity sensitivity = static_cast(m_mouseSensitivityButtons->checkedId()); bool autostart = ui->autostartChkBox->isChecked(); bool showInfoWindow = ui->infoWindowChkBox->isChecked(); m_corona->universalSettings()->setMouseSensitivity(sensitivity); m_corona->universalSettings()->setAutostart(autostart); m_corona->universalSettings()->setShowInfoWindow(showInfoWindow); m_corona->universalSettings()->setScreenTrackerInterval(ui->screenTrackerSpinBox->value()); //! Update Layouts QStringList knownActivities = activities(); QTemporaryDir layoutTempDir; qDebug() << "Temporary Directory ::: " << layoutTempDir.path(); QStringList fromRenamePaths; QStringList toRenamePaths; QStringList toRenameNames; QString switchToLayout; QHash activeLayoutsToRename; //! remove layouts that have been removed from the user foreach (auto initLayout, m_initLayoutPaths) { if (!idExistsInModel(initLayout)) { QFile(initLayout).remove(); if (m_layouts.contains(initLayout)) { Layout *removedLayout = m_layouts.take(initLayout); delete removedLayout; } } } for (int i = 0; i < m_model->rowCount(); ++i) { QString id = m_model->data(m_model->index(i, IDCOLUMN), Qt::DisplayRole).toString(); QString color = m_model->data(m_model->index(i, COLORCOLUMN), Qt::BackgroundRole).toString(); QString textColor = m_model->data(m_model->index(i, COLORCOLUMN), Qt::UserRole).toString(); QString name = m_model->data(m_model->index(i, NAMECOLUMN), Qt::DisplayRole).toString(); bool locked = m_model->data(m_model->index(i, NAMECOLUMN), Qt::UserRole).toBool(); bool menu = m_model->data(m_model->index(i, MENUCOLUMN), Qt::DisplayRole).toString() == CheckMark; QStringList lActivities = m_model->data(m_model->index(i, ACTIVITYCOLUMN), Qt::UserRole).toStringList(); QStringList cleanedActivities; //!update only activities that are valid foreach (auto activity, lActivities) { if (knownActivities.contains(activity)) { cleanedActivities.append(activity); } } //qDebug() << i << ". " << id << " - " << color << " - " << name << " - " << menu << " - " << lActivities; Layout *activeLayout = m_corona->layoutManager()->activeLayout(m_layouts[id]->name()); Layout *layout = activeLayout ? activeLayout : m_layouts[id]; //! unlock read-only layout if (!layout->isWritable()) { layout->unlock(); } if (color.startsWith("/")) { //it is image file in such case if (color != layout->background()) { layout->setBackground(color); } if (layout->textColor() != textColor) { layout->setTextColor(textColor); } } else { if (color != layout->color()) { layout->setColor(color); layout->setBackground(QString()); layout->setTextColor(QString()); } } if (layout->showInMenu() != menu) { layout->setShowInMenu(menu); } if (layout->activities() != cleanedActivities) { layout->setActivities(cleanedActivities); } //! If the layout name changed OR the layout path is a temporary one if (layout->name() != name || (id.startsWith("/tmp/"))) { //! If the layout is Active in MultipleLayouts if (m_corona->layoutManager()->memoryUsage() == Dock::MultipleLayouts && activeLayout) { qDebug() << " Active Layout Should Be Renamed From : " << layout->name() << " TO :: " << name; activeLayoutsToRename[name] = layout; } QString tempFile = layoutTempDir.path() + "/" + QString(layout->name() + ".layout.latte"); qDebug() << "new temp file ::: " << tempFile; if ((m_corona->layoutManager()->memoryUsage() == Dock::SingleLayout) && (layout->name() == m_corona->layoutManager()->currentLayoutName())) { switchToLayout = name; } layout = m_layouts.take(id); delete layout; QFile(id).rename(tempFile); fromRenamePaths.append(id); toRenamePaths.append(tempFile); toRenameNames.append(name); } } //! this is necessary in case two layouts have to swap names //! so we copy first the layouts in a temp directory and afterwards all //! together we move them in the official layout directory for (int i = 0; i < toRenamePaths.count(); ++i) { QString newFile = QDir::homePath() + "/.config/latte/" + toRenameNames[i] + ".layout.latte"; QFile(toRenamePaths[i]).rename(newFile); Layout *nLayout = new Layout(this, newFile); m_layouts[newFile] = nLayout; for (int j = 0; j < m_model->rowCount(); ++j) { QString tId = m_model->data(m_model->index(j, IDCOLUMN), Qt::DisplayRole).toString(); if (tId == fromRenamePaths[i]) { m_model->setData(m_model->index(j, IDCOLUMN), newFile, Qt::DisplayRole); m_initLayoutPaths.append(newFile); QFont font = qvariant_cast(m_model->data(m_model->index(j, NAMECOLUMN), Qt::FontRole)); font.setItalic(false); m_model->setData(m_model->index(j, NAMECOLUMN), font, Qt::FontRole); } } } QString orphanedLayout; if (m_corona->layoutManager()->memoryUsage() == Dock::MultipleLayouts) { foreach (auto newLayoutName, activeLayoutsToRename.keys()) { qDebug() << " Active Layout Is Renamed From : " << activeLayoutsToRename[newLayoutName]->name() << " TO :: " << newLayoutName; Layout *layout = activeLayoutsToRename[newLayoutName]; layout->renameLayout(newLayoutName); //! that means it is an active layout for orphaned Activities if (layout->activities().isEmpty()) { orphanedLayout = newLayoutName; } } } //! lock layouts in the end when the user has chosen it for (int i = 0; i < m_model->rowCount(); ++i) { QString id = m_model->data(m_model->index(i, IDCOLUMN), Qt::DisplayRole).toString(); QString name = m_model->data(m_model->index(i, NAMECOLUMN), Qt::DisplayRole).toString(); bool locked = m_model->data(m_model->index(i, NAMECOLUMN), Qt::UserRole).toBool(); Layout *activeLayout = m_corona->layoutManager()->activeLayout(m_layouts[id]->name()); Layout *layout = activeLayout ? activeLayout : m_layouts[id]; if (layout && locked && layout->isWritable()) { layout->lock(); } } m_corona->layoutManager()->loadLayouts(); Latte::Dock::LayoutsMemoryUsage inMemoryOption = static_cast(m_inMemoryButtons->checkedId()); if (m_corona->layoutManager()->memoryUsage() != inMemoryOption) { Dock::LayoutsMemoryUsage previousMemoryUsage = m_corona->layoutManager()->memoryUsage(); m_corona->layoutManager()->setMemoryUsage(inMemoryOption); QVariant value = m_model->data(m_model->index(ui->layoutsView->currentIndex().row(), NAMECOLUMN), Qt::DisplayRole); QString layoutName = value.toString(); m_corona->layoutManager()->switchToLayout(layoutName, previousMemoryUsage); } else { if (!switchToLayout.isEmpty()) { m_corona->layoutManager()->switchToLayout(switchToLayout); } else if (m_corona->layoutManager()->memoryUsage() == Dock::MultipleLayouts) { m_corona->layoutManager()->syncMultipleLayoutsToActivities(orphanedLayout); } } return true; } bool SettingsDialog::idExistsInModel(QString id) { for (int i = 0; i < m_model->rowCount(); ++i) { QString rowId = m_model->data(m_model->index(i, IDCOLUMN), Qt::DisplayRole).toString(); if (rowId == id) { return true; } } return false; } bool SettingsDialog::nameExistsInModel(QString name) { for (int i = 0; i < m_model->rowCount(); ++i) { QString rowName = m_model->data(m_model->index(i, NAMECOLUMN), Qt::DisplayRole).toString(); if (rowName == name) { return true; } } return false; } int SettingsDialog::ascendingRowFor(QString name) { for (int i = 0; i < m_model->rowCount(); ++i) { QString rowName = m_model->data(m_model->index(i, NAMECOLUMN), Qt::DisplayRole).toString(); if (rowName.toUpper() > name.toUpper()) { return i; } } return m_model->rowCount(); } QString SettingsDialog::uniqueTempDirectory() { QTemporaryDir tempDir; tempDir.setAutoRemove(false); m_tempDirectories.append(tempDir.path()); return tempDir.path(); } QString SettingsDialog::uniqueLayoutName(QString name) { int pos_ = name.lastIndexOf(QRegExp(QString("[-][0-9]+"))); if (nameExistsInModel(name) && pos_ > 0) { name = name.left(pos_); } int i = 2; QString namePart = name; while (nameExistsInModel(name)) { name = namePart + "-" + QString::number(i); i++; } return name; } }//end of namespace diff --git a/shell/package/contents/configuration/BehaviorConfig.qml b/shell/package/contents/configuration/BehaviorConfig.qml index c0f3b4cd..2d7f03bc 100644 --- a/shell/package/contents/configuration/BehaviorConfig.qml +++ b/shell/package/contents/configuration/BehaviorConfig.qml @@ -1,505 +1,508 @@ /* * Copyright 2016 Smith AR * Michail Vourlakos * * This file is part of Latte-Dock * * Latte-Dock is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * Latte-Dock is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ import QtQuick 2.0 import QtQuick.Controls 1.4 import QtQuick.Layouts 1.3 import QtGraphicalEffects 1.0 import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.plasma.components 2.0 as PlasmaComponents import org.kde.plasma.components 3.0 as PlasmaComponents3 import org.kde.plasma.plasmoid 2.0 import org.kde.latte 0.1 as Latte import "../controls" as LatteExtraControls PlasmaComponents.Page { Layout.maximumWidth: content.width + content.Layout.leftMargin * 2 Layout.maximumHeight: content.height + units.smallSpacing * 2 property alias dockTypeSelection: _dockTypeSelection ColumnLayout { id: content width: dialog.maxWidth - 2 * Layout.leftMargin spacing: dialog.subGroupSpacing anchors.horizontalCenter: parent.horizontalCenter Layout.leftMargin: units.smallSpacing * 2 //! BEGIN: Inline Dock/Panel Type, it is used only when the secondary window //! overlaps the main dock config window ColumnLayout { Layout.fillWidth: true spacing: units.smallSpacing Layout.rightMargin: units.smallSpacing * 2 Layout.topMargin: units.smallSpacing visible: plasmoid.configuration.advanced && dockConfig.showInlineProperties Header { text: i18n("Type") } TypeSelection{ id: _dockTypeSelection horizontal: true } } //! END: Inline Dock/Panel Type //! BEGIN: Location ColumnLayout { Layout.fillWidth: true spacing: units.smallSpacing Layout.rightMargin: units.smallSpacing * 2 Layout.topMargin: units.smallSpacing Header { text: i18n("Location") } RowLayout { id: screenRow Layout.fillWidth: true Layout.leftMargin: units.smallSpacing * 2 Layout.rightMargin: units.smallSpacing * 2 spacing: 1 visible: true function updateScreens() { if (dock.screens.length > 1) screenRow.visible = true; else screenRow.visible = false; var screens = [] var rtlSpace = Qt.application.layoutDirection === Qt.RightToLeft ? " " : ""; screens.push(rtlSpace + i18n("On Primary")); //check if the screen exists, it is used in cases Latte is moving //the dock automatically to primaryScreen in order for the user //to has always a dock with tasks shown var screenExists = false for (var i = 0; i < dock.screens.length; i++) { if (dock.screens[i].name === dock.currentScreen) screenExists = true; } if (!screenExists && !dock.onPrimary) screens.push(rtlSpace + dock.currentScreen); for (var i = 0; i < dock.screens.length; i++) { screens.push(rtlSpace + dock.screens[i].name) } screenCmb.model = screens; if (dock.onPrimary) { screenCmb.currentIndex = 0; } else { screenCmb.currentIndex = screenCmb.find(dock.currentScreen); } console.log(dock.currentScreen); } Connections{ target: dockConfig onShowSignal: screenRow.updateScreens(); } PlasmaComponents.Label { text: i18n("Screen:") Layout.alignment: Qt.AlignRight } PlasmaComponents3.ComboBox { id: screenCmb Layout.fillWidth: true Component.onCompleted: screenRow.updateScreens(); //they are used to restore the index when the screen edge //is occuppied property bool acceptedIndex: true property int previousIndex: -1 onCurrentIndexChanged: { //it is used to restore the index when the screen edge //is occuppied if (!acceptedIndex) { acceptedIndex = true; currentIndex = previousIndex; } } onActivated: { previousIndex = currentIndex; if (index === 0) { var succeed = dock.setCurrentScreen("primary"); dock.onPrimary = true; acceptedIndex = true; } else if (index>0 && (index !== find(dock.currentScreen) || dock.onPrimary)) { console.log("current index changed!!! :"+ index); console.log("screen must be changed..."); var succeed = dock.setCurrentScreen(textAt(index)); if(succeed) { dock.onPrimary = false; } else { console.log("the edge is already occupied!!!"); acceptedIndex = false; } } } } } RowLayout { id: locationLayout Layout.fillWidth: true Layout.leftMargin: units.smallSpacing * 2 Layout.rightMargin: units.smallSpacing * 2 LayoutMirroring.enabled: false spacing: 1 Connections{ target: dock onDockLocationChanged: locationLayout.lockReservedEdges(); onDocksCountChanged: locationLayout.lockReservedEdges(); } ExclusiveGroup { id: locationGroup property bool inStartup: true onCurrentChanged: { if (current.checked && !inStartup) { dock.hideDockDuringLocationChange(current.edge); } inStartup = false; } } function lockReservedEdges() { var buttons = visibleChildren var edges = dock.freeEdges() for (var i = 0; i < buttons.length; i++) { buttons[i].enabled = buttons[i].checked || freeEdge(buttons[i].edge, edges) } } function freeEdge(edge, edges) { for (var i = 0; i < edges.length; i++) { if (edges[i] === edge) return true } return false } PlasmaComponents.Button { Layout.fillWidth: true text: i18nc("bottom location", "Bottom") iconSource: "arrow-down" checked: dock.location === edge checkable: true enabled: checked || locationLayout.freeEdge(edge, dock.freeEdges()) exclusiveGroup: locationGroup readonly property int edge: PlasmaCore.Types.BottomEdge } PlasmaComponents.Button { Layout.fillWidth: true text: i18nc("left location", "Left") iconSource: "arrow-left" checked: dock.location === edge checkable: true enabled: checked || locationLayout.freeEdge(edge, dock.freeEdges()) exclusiveGroup: locationGroup readonly property int edge: PlasmaCore.Types.LeftEdge } PlasmaComponents.Button { Layout.fillWidth: true text: i18nc("top location", "Top") iconSource: "arrow-up" checked: dock.location === edge checkable: true enabled: checked || locationLayout.freeEdge(edge, dock.freeEdges()) exclusiveGroup: locationGroup readonly property int edge: PlasmaCore.Types.TopEdge } PlasmaComponents.Button { Layout.fillWidth: true text: i18nc("right location", "Right") iconSource: "arrow-right" checked: dock.location === edge checkable: true enabled: checked || locationLayout.freeEdge(edge, dock.freeEdges()) exclusiveGroup: locationGroup readonly property int edge: PlasmaCore.Types.RightEdge } } } //! END: Location //! BEGIN: Alignment ColumnLayout { Layout.fillWidth: true spacing: units.smallSpacing Layout.rightMargin: units.smallSpacing * 2 Header { text: i18n("Alignment") } RowLayout { Layout.fillWidth: true Layout.leftMargin: units.smallSpacing * 2 Layout.rightMargin: units.smallSpacing * 2 LayoutMirroring.enabled: false spacing: 1 property int panelPosition: plasmoid.configuration.panelPosition onPanelPositionChanged: { if (panelPosition === Latte.Dock.Justify) dock.addInternalViewSplitter() else dock.removeInternalViewSplitter() } Component.onCompleted: { if (panelPosition === Latte.Dock.Justify) dock.addInternalViewSplitter() else dock.removeInternalViewSplitter() } ExclusiveGroup { id: alignmentGroup onCurrentChanged: { if (current.checked) plasmoid.configuration.panelPosition = current.position } } PlasmaComponents.Button { Layout.fillWidth: true text: panelIsVertical ? i18nc("top alignment", "Top") : i18nc("left alignment", "Left") iconSource: panelIsVertical ? "format-align-vertical-top" : "format-justify-left" checked: parent.panelPosition === position checkable: true exclusiveGroup: alignmentGroup property int position: panelIsVertical ? Latte.Dock.Top : Latte.Dock.Left } PlasmaComponents.Button { Layout.fillWidth: true text: i18nc("center alignment", "Center") iconSource: panelIsVertical ? "format-align-vertical-center" : "format-justify-center" checked: parent.panelPosition === position checkable: true exclusiveGroup: alignmentGroup property int position: Latte.Dock.Center } PlasmaComponents.Button { Layout.fillWidth: true text: panelIsVertical ? i18nc("bottom alignment", "Bottom") : i18nc("right alignment", "Right") iconSource: panelIsVertical ? "format-align-vertical-bottom" : "format-justify-right" checked: parent.panelPosition === position checkable: true exclusiveGroup: alignmentGroup property int position: panelIsVertical ? Latte.Dock.Bottom : Latte.Dock.Right } PlasmaComponents.Button { Layout.fillWidth: true text: i18nc("justify alignment", "Justify") iconSource: "format-justify-fill" checked: parent.panelPosition === position checkable: true exclusiveGroup: alignmentGroup property int position: Latte.Dock.Justify } } } //! END: Alignment //! BEGIN: Visibility ColumnLayout { Layout.fillWidth: true spacing: units.smallSpacing Layout.rightMargin: units.smallSpacing * 2 Header { text: i18n("Visibility") } GridLayout { width: parent.width rowSpacing: 1 columnSpacing: 1 Layout.leftMargin: units.smallSpacing * 2 Layout.rightMargin: units.smallSpacing * 2 columns: 2 property int mode: dock.visibility.mode ExclusiveGroup { id: visibilityGroup onCurrentChanged: { if (current.checked) dock.visibility.mode = current.mode } } PlasmaComponents.Button { Layout.fillWidth: true text: i18n("Always Visible") checked: parent.mode === mode checkable: true exclusiveGroup: visibilityGroup property int mode: Latte.Dock.AlwaysVisible } PlasmaComponents.Button { Layout.fillWidth: true text: i18n("Auto Hide") checked: parent.mode === mode checkable: true exclusiveGroup: visibilityGroup property int mode: Latte.Dock.AutoHide } PlasmaComponents.Button { Layout.fillWidth: true text: i18n("Dodge Active") checked: parent.mode === mode checkable: true exclusiveGroup: visibilityGroup property int mode: Latte.Dock.DodgeActive } PlasmaComponents.Button { Layout.fillWidth: true text: i18n("Dodge Maximized") checked: parent.mode === mode checkable: true exclusiveGroup: visibilityGroup property int mode: Latte.Dock.DodgeMaximized } PlasmaComponents.Button { Layout.fillWidth: true text: i18n("Dodge All Windows") checked: parent.mode === mode checkable: true exclusiveGroup: visibilityGroup property int mode: Latte.Dock.DodgeAllWindows } PlasmaComponents.Button { Layout.fillWidth: true text: i18n("Windows Go Below") checked: parent.mode === mode checkable: true exclusiveGroup: visibilityGroup property int mode: Latte.Dock.WindowsGoBelow } } } //! END: Visibility //! BEGIN: Delay ColumnLayout { Layout.fillWidth: true Layout.rightMargin: units.smallSpacing * 2 spacing: units.smallSpacing enabled: !(dock.visibility.mode === Latte.Dock.AlwaysVisible || dock.visibility.mode === Latte.Dock.WindowsGoBelow) Header { Layout.fillWidth: true text: i18n("Delay") } RowLayout { Layout.fillWidth: false Layout.leftMargin: units.smallSpacing * 2 Layout.rightMargin: units.smallSpacing * 2 Layout.alignment: Qt.AlignHCenter spacing: 2 PlasmaComponents.Label { Layout.fillWidth: false - Layout.rightMargin: units.smallSpacing + Layout.rightMargin: Qt.application.layoutDirection === Qt.RightToLeft ? 0 : units.smallSpacing + Layout.leftMargin: Qt.application.layoutDirection === Qt.RightToLeft ? units.smallSpacing : 0 horizontalAlignment: Text.AlignRight text: i18n("Show") } LatteExtraControls.TextField { Layout.preferredWidth: width text: dock.visibility.timerShow onValueChanged: { dock.visibility.timerShow = value } } PlasmaComponents.Label { Layout.fillWidth: false - Layout.leftMargin: units.largeSpacing - Layout.rightMargin: units.smallSpacing + Layout.leftMargin: Qt.application.layoutDirection === Qt.RightToLeft ? + units.smallSpacing : units.largeSpacing + Layout.rightMargin: Qt.application.layoutDirection === Qt.RightToLeft ? + units.largeSpacing : units.smallSpacing horizontalAlignment: Text.AlignRight text: i18n("Hide") } LatteExtraControls.TextField{ Layout.preferredWidth: width text: dock.visibility.timerHide onValueChanged: { dock.visibility.timerHide = value } } } } //! END: Delay PlasmaComponents.Label{ id: bottomMarginSpacer text:" " } } }