Added a 'dynamic view' option, which allows to switch from a 'compact' or 'details' view to an 'icons' view if most of the files in the directory are images or videos. It reverts to the previous view mode when we switch to a directory which doesn't meet that criteria.
The view mode is only changed once so users don't have to undo that for specific folders when they don't want icon view.
A setting is added in the "Display style" section of the general view setting page.
BUG: 491139
<whatsthis context="@info:whatsthis">The last time these properties were changed by the user.</whatsthis>
</entry>
+ <entry name="DynamicViewPassed" type="Bool">
+ <label context="@label">View mode changed once by dynamic view</label>
+ <default>false</default>
+ </entry>
+
<!-- Obsolete - replaced by VisibleRoles -->
<entry name="AdditionalInfo" type="StringList">
<label context="@label">Additional Information</label>
<label>Also hide files with application/x-trash mimetype</label>
<default>false</default>
</entry>
+ <entry name="DynamicView" type="Bool">
+ <label>Enable dynamic view</label>
+ <default>false</default>
+ </entry>
</group>
<group name="Notification Messages">
<entry name="ConfirmOpenManyFolders" type="Bool">
localViewPropsLabel->setWordWrap(true);
localViewPropsLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
+ m_dynamicView = new QCheckBox(i18nc("option:check", "Use icons view mode for locations which mostly contain media files"));
+
QButtonGroup *viewGroup = new QButtonGroup(this);
viewGroup->addButton(m_globalViewProps);
viewGroup->addButton(m_localViewProps);
topLayout->addRow(QString(), globalViewPropsLabel);
topLayout->addRow(QString(), m_localViewProps);
topLayout->addRow(QString(), localViewPropsLabel);
+ topLayout->addRow(QString(), m_dynamicView);
topLayout->addItem(new QSpacerItem(0, Dolphin::VERTICAL_SPACER_HEIGHT, QSizePolicy::Fixed, QSizePolicy::Fixed));
connect(m_showSelectionToggle, &QCheckBox::toggled, this, &GeneralViewSettingsPage::changed);
connect(m_renameInline, &QCheckBox::toggled, this, &GeneralViewSettingsPage::changed);
connect(m_hideXtrashFiles, &QCheckBox::toggled, this, &GeneralViewSettingsPage::changed);
+ connect(m_dynamicView, &QCheckBox::toggled, this, &GeneralViewSettingsPage::changed);
connect(m_doubleClickViewCustomAction, &QLineEdit::textChanged, this, &GeneralViewSettingsPage::changed);
connect(m_doubleClickViewComboBox, qOverload<int>(&QComboBox::currentIndexChanged), this, &GeneralViewSettingsPage::changed);
connect(m_doubleClickViewComboBox, qOverload<int>(&QComboBox::currentIndexChanged), this, &GeneralViewSettingsPage::updateCustomActionVisibility);
settings->setShowSelectionToggle(m_showSelectionToggle->isChecked());
settings->setRenameInline(m_renameInline->isChecked());
settings->setHideXTrashFile(m_hideXtrashFiles->isChecked());
+ settings->setDynamicView(m_dynamicView->isChecked());
settings->setAutoExpandFolders(m_autoExpandFolders->isChecked());
settings->setBrowseThroughArchives(m_openArchivesAsFolder->isChecked());
settings->setDoubleClickViewCustomAction(m_doubleClickViewCustomAction->text());
m_showSelectionToggle->setChecked(GeneralSettings::showSelectionToggle());
m_renameInline->setChecked(GeneralSettings::renameInline());
m_hideXtrashFiles->setChecked(GeneralSettings::hideXTrashFile());
+ m_dynamicView->setChecked(GeneralSettings::dynamicView());
m_localViewProps->setChecked(!useGlobalViewProps);
m_globalViewProps->setChecked(useGlobalViewProps);
QCheckBox *m_openArchivesAsFolder = nullptr;
QCheckBox *m_autoExpandFolders = nullptr;
QCheckBox *m_hideXtrashFiles = nullptr;
+ QCheckBox *m_dynamicView = nullptr;
QComboBox *m_doubleClickViewComboBox = nullptr;
QLineEdit *m_doubleClickViewCustomAction = nullptr;
QLabel *m_doubleClickViewCustomActionInfo = nullptr;
*/
#include "dolphinmainwindow.h"
+#include "dolphin_generalsettings.h"
#include "dolphinnewfilemenu.h"
#include "dolphintabpage.h"
#include "dolphintabwidget.h"
#include "dolphinviewcontainer.h"
-#include "dolphin_generalsettings.h"
#include "kitemviews/kfileitemmodel.h"
#include "kitemviews/kfileitemmodelrolesupdater.h"
#include "kitemviews/kitemlistcontainer.h"
#include "kitemviews/kitemlistwidget.h"
#include "testdir.h"
#include "views/dolphinitemlistview.h"
+#include "views/viewproperties.h"
#include <KActionCollection>
#include <KConfig>
void testAutoSaveSession();
void testInlineRename();
void testThumbnailAfterRename();
+ void testViewModeAfterDynamicView();
void cleanupTestCase();
private:
QCOMPARE(view->m_model->count(), 1);
}
+void DolphinMainWindowTest::testViewModeAfterDynamicView()
+{
+ GeneralSettings *settings = GeneralSettings::self();
+ settings->setDynamicView(true);
+ settings->save();
+
+ // prepare test data
+ QScopedPointer<TestDir> testDir{new TestDir()};
+ QString testDirUrl(QDir::cleanPath(testDir->url().toString()));
+ testDir->createDir("a");
+ QImage testImage(256, 256, QImage::Format_Mono);
+ testImage.setColorCount(1);
+ testImage.setColor(0, qRgba(255, 0, 0, 255)); // Index #0 = Red
+ for (short x = 0; x < 256; ++x) {
+ for (short y = 0; y < 256; ++y) {
+ testImage.setPixel(x, y, 0);
+ }
+ }
+ testImage.save(testDir->url().path() + "/a/1.jpg");
+
+ // open test dir and set default view mode to "Details"
+ m_mainWindow->openDirectories({testDirUrl}, false);
+ DolphinView *view = m_mainWindow->activeViewContainer()->view();
+ QSignalSpy viewDirectoryLoadingCompletedSpy(view, &DolphinView::directoryLoadingCompleted);
+ QSignalSpy modelDirectoryLoadingCompletedSpy(view->m_model, &KFileItemModel::directoryLoadingCompleted);
+ m_mainWindow->show();
+ QVERIFY(viewDirectoryLoadingCompletedSpy.wait());
+ QVERIFY(QTest::qWaitForWindowExposed(m_mainWindow.data()));
+ QVERIFY(m_mainWindow->isVisible());
+ m_mainWindow->actionCollection()->action(QStringLiteral("details"))->trigger();
+ QCOMPARE(view->m_mode, DolphinView::DetailsView);
+
+ // move to child folder and check that dynamic view changed view mode to icons
+ m_mainWindow->openFiles({testDirUrl + "/a"}, false);
+ view->m_model->loadDirectory(QUrl(testDirUrl + "/a"));
+ view->setUrl(QUrl(testDirUrl + "/a"));
+ QVERIFY(modelDirectoryLoadingCompletedSpy.wait());
+ QCOMPARE(view->m_mode, DolphinView::IconsView);
+
+ // go back to parent folder and check that view mode reverted to details
+ m_mainWindow->actionCollection()->action(KStandardAction::name(KStandardAction::Back))->trigger();
+ view->m_model->loadDirectory(testDir->url());
+ view->setUrl(testDir->url());
+ QVERIFY(modelDirectoryLoadingCompletedSpy.wait());
+ QCOMPARE(view->m_mode, DolphinView::DetailsView);
+
+ // test for local views
+ settings->setGlobalViewProps(false);
+ settings->save();
+
+ // go to child folder and check DynamicViewPassed key in view properties as well as view mode
+ m_mainWindow->openFiles({testDirUrl + "/a"}, false);
+ view->m_model->loadDirectory(QUrl(testDirUrl + "/a"));
+ view->setUrl(QUrl(testDirUrl + "/a"));
+ QVERIFY(modelDirectoryLoadingCompletedSpy.wait());
+ QCOMPARE(view->m_mode, DolphinView::IconsView);
+ QTest::qWait(100);
+ QVERIFY(ViewProperties(view->viewPropertiesUrl()).dynamicViewPassed());
+
+ // change view mode of child folder to "Details"
+ m_mainWindow->actionCollection()->action(QStringLiteral("details"))->trigger();
+ QCOMPARE(view->m_mode, DolphinView::DetailsView);
+
+ // go back to parent folder
+ m_mainWindow->actionCollection()->action(KStandardAction::name(KStandardAction::Back))->trigger();
+ view->m_model->loadDirectory(testDir->url());
+ view->setUrl(testDir->url());
+ QVERIFY(modelDirectoryLoadingCompletedSpy.wait());
+ QCOMPARE(view->m_mode, DolphinView::DetailsView);
+ QVERIFY(!ViewProperties(view->viewPropertiesUrl()).dynamicViewPassed());
+
+ // go to child folder and make sure view mode change to "Details" is permanent
+ m_mainWindow->openFiles({testDirUrl + "/a"}, false);
+ view->m_model->loadDirectory(QUrl(testDirUrl + "/a"));
+ view->setUrl(QUrl(testDirUrl + "/a"));
+ QVERIFY(modelDirectoryLoadingCompletedSpy.wait());
+ QCOMPARE(view->m_mode, DolphinView::DetailsView);
+ QVERIFY(ViewProperties(view->viewPropertiesUrl()).dynamicViewPassed());
+}
+
void DolphinMainWindowTest::cleanupTestCase()
{
m_mainWindow->showNormal();
Q_EMIT directoryLoadingCompleted();
+ applyDynamicView();
updatePlaceholderLabel();
updateWritableState();
}
}
}
+void DolphinView::applyDynamicView()
+{
+ ViewProperties props(viewPropertiesUrl());
+ /* return early if:
+ * - dynamic view is not enabled
+ * - the current view mode is already Icon View
+ * - dynamic view has previously changed the view mode
+ */
+ if (!GeneralSettings::dynamicView() || m_mode == IconsView || props.dynamicViewPassed()) {
+ return;
+ }
+
+ uint imageAndVideoCount = 0;
+ uint checkedItems = 0;
+ const uint totalItems = itemsCount();
+ const KFileItemList itemList = items();
+ bool applyDynamicView = false;
+
+ for (const auto &file : itemList) {
+ ++checkedItems;
+ const QString type = file.mimetype().slice(0, 5);
+
+ if (type == "image" || type == "video") {
+ ++imageAndVideoCount;
+ // if 2/3 or more of the items are images/videos, dynamic view should be applied
+ applyDynamicView = imageAndVideoCount >= (totalItems * 2 / 3);
+ if (applyDynamicView) {
+ break;
+ }
+ } else if (checkedItems - imageAndVideoCount > totalItems / 3) {
+ // if more than a third of the checked files are not media files, return
+ return;
+ }
+ }
+
+ if (!applyDynamicView) {
+ return;
+ }
+
+ props.setAutoSaveEnabled(!GeneralSettings::globalViewProps());
+ props.setDynamicViewPassed(true);
+ props.setViewMode(IconsView);
+ applyViewProperties(props);
+}
+
void DolphinView::pasteToUrl(const QUrl &url)
{
KIO::PasteJob *job = KIO::paste(QApplication::clipboard()->mimeData(), url);
*/
void applyModeToView();
+ /**
+ * Changes the current view based on the content of the directory.
+ */
+ void applyDynamicView();
+
enum Selection { HasSelection, NoSelection };
/**
* Helper method for DolphinView::requestStatusBarText().
return m_node->sortHiddenLast();
}
+void ViewProperties::setDynamicViewPassed(bool dynamicViewPassed)
+{
+ if (m_node->dynamicViewPassed() != dynamicViewPassed) {
+ m_node->setDynamicViewPassed(dynamicViewPassed);
+ update();
+ }
+}
+
+bool ViewProperties::dynamicViewPassed() const
+{
+ return m_node->dynamicViewPassed();
+}
+
void ViewProperties::setVisibleRoles(const QList<QByteArray> &roles)
{
if (roles == visibleRoles()) {
void setSortHiddenLast(bool hiddenLast);
bool sortHiddenLast() const;
+ void setDynamicViewPassed(bool dynamicViewPassed);
+ bool dynamicViewPassed() const;
+
/**
* Sets the additional information for the current set view-mode.
* Note that the additional-info property is the only property where