Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ compile_commands.json
build/

# Visual Studio
.vscode/
*.sln
*.suo
*.vcxproj
Expand Down
1 change: 1 addition & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
* Allow filtering tilesets by name in the tileset dock (with dogboydog, #4239)
* Allow changing the values of number inputs using expressions (with dogboydog, #4234)
* Added support for SVG 1.2 / CSS blending modes to layers (#3932)
* Added support for natural sorting of project files (by Edgar Jr. San Martin, #4284)
* Added button to toggle Terrain Brush to full tile mode (by Finlay Pearson, #3407)
* Added square selection and expand-from-center to Rectangular Select tool (#4201)
* Added status info for various Stamp Brush, Terrain Brush and Eraser modes (#3092, #4201)
Expand Down
10 changes: 8 additions & 2 deletions src/tiled/locatorwidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,12 @@

#include "documentmanager.h"
#include "filteredit.h"
#include "preferences.h"
#include "projectmanager.h"
#include "utils.h"

#include <QApplication>
#include <QCollator>
#include <QDir>
#include <QKeyEvent>
#include <QPainter>
Expand Down Expand Up @@ -376,13 +378,17 @@ void FileLocatorSource::setFilterWords(const QStringList &words)
auto projectModel = ProjectManager::instance()->projectModel();
auto matches = projectModel->findFiles(words);

std::stable_sort(matches.begin(), matches.end(), [] (const ProjectModel::Match &a, const ProjectModel::Match &b) {
QCollator collator;
collator.setCaseSensitivity(Qt::CaseInsensitive);
collator.setNumericMode(Preferences::instance()->naturalSorting());

std::stable_sort(matches.begin(), matches.end(), [&] (const ProjectModel::Match &a, const ProjectModel::Match &b) {
// Sort based on score first
if (a.score != b.score)
return a.score > b.score;

// If score is the same, sort alphabetically
return a.relativePath().compare(b.relativePath(), Qt::CaseInsensitive) < 0;
return collator.compare(a.relativePath(), b.relativePath()) < 0;
});

mDelegate->setWords(words);
Expand Down
11 changes: 11 additions & 0 deletions src/tiled/preferences.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -662,6 +662,17 @@ bool Preferences::restoreSessionOnStartup() const
return get("Startup/RestorePreviousSession", true);
}

bool Preferences::naturalSorting() const
{
return get("Project/NaturalSorting", true);
}

void Preferences::setNaturalSorting(bool enabled)
{
setValue(QLatin1String("Project/NaturalSorting"), enabled);
emit naturalSortingChanged(enabled);
}

void Preferences::addToRecentFileList(const QString &fileName, QStringList& files)
{
// Remember the file by its absolute file path (not the canonical one,
Expand Down
5 changes: 5 additions & 0 deletions src/tiled/preferences.h
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,9 @@ class TILED_EDITOR_EXPORT Preferences : public QSettings
void setLastSession(const QString &fileName);
bool restoreSessionOnStartup() const;

bool naturalSorting() const;
void setNaturalSorting(bool enabled);

bool checkForUpdates() const;
void setCheckForUpdates(bool on);

Expand Down Expand Up @@ -252,6 +255,8 @@ public slots:
void recentFilesChanged();
void recentProjectsChanged();

void naturalSortingChanged(bool enabled);

void checkForUpdatesChanged(bool on);
void displayNewsChanged(bool on);

Expand Down
3 changes: 3 additions & 0 deletions src/tiled/preferencesdialog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,8 @@ PreferencesDialog::PreferencesDialog(QWidget *parent)
preferences, &Preferences::setSafeSavingEnabled);
connect(mUi->exportOnSave, &QCheckBox::toggled,
preferences, &Preferences::setExportOnSave);
connect(mUi->naturalSorting, &QCheckBox::toggled,
preferences, &Preferences::setNaturalSorting);

connect(mUi->embedTilesets, &QCheckBox::toggled, preferences, [preferences] (bool value) {
preferences->setExportOption(Preferences::EmbedTilesets, value);
Expand Down Expand Up @@ -218,6 +220,7 @@ void PreferencesDialog::fromPreferences()
mUi->restoreSession->setChecked(prefs->restoreSessionOnStartup());
mUi->safeSaving->setChecked(prefs->safeSavingEnabled());
mUi->exportOnSave->setChecked(prefs->exportOnSave());
mUi->naturalSorting->setChecked(prefs->naturalSorting());

mUi->embedTilesets->setChecked(prefs->exportOption(Preferences::EmbedTilesets));
mUi->detachTemplateInstances->setChecked(prefs->exportOption(Preferences::DetachTemplateInstances));
Expand Down
11 changes: 11 additions & 0 deletions src/tiled/preferencesdialog.ui
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,16 @@
</property>
</widget>
</item>
<item row="7" column="0" colspan="2">
<widget class="QCheckBox" name="naturalSorting">
<property name="toolTip">
<string>Sort files numerically (d3, d20) instead of alphabetically (d20, d3)</string>
</property>
<property name="text">
<string>Use natural sorting in Projects view</string>
</property>
</widget>
</item>
<item row="4" column="1" colspan="2">
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
Expand Down Expand Up @@ -692,6 +702,7 @@
<tabstop>gridMajorY</tabstop>
<tabstop>objectLineWidth</tabstop>
<tabstop>openGL</tabstop>
<tabstop>naturalSorting</tabstop>
<tabstop>objectSelectionBehaviorCombo</tabstop>
<tabstop>preciseTileObjectSelection</tabstop>
<tabstop>wheelZoomsByDefault</tabstop>
Expand Down
79 changes: 41 additions & 38 deletions src/tiled/projectdock.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,17 +62,16 @@ class ProjectView final : public QTreeView
*/
QSize sizeHint() const override;

void setModel(QAbstractItemModel *model) override;
ProjectModel *model() const { return mProjectModel; }

// TODO: Add 'select by file name'
ProjectModel *projectModel() const { return mProjectModel; }

QStringList expandedPaths() const { return mExpandedPaths.values(); }
void setExpandedPaths(const QStringList &paths);
void addExpandedPath(const QString &path);

void selectPath(const QString &path);

QString filePath(const QModelIndex &index) const;

protected:
void contextMenuEvent(QContextMenuEvent *event) override;

Expand All @@ -83,6 +82,7 @@ class ProjectView final : public QTreeView
void restoreExpanded(const QModelIndex &parent);

ProjectModel *mProjectModel;
ProjectProxyModel *mProxyModel;
QSet<QString> mExpandedPaths;
QString mSelectedPath;
int mScrollBarValue = 0;
Expand Down Expand Up @@ -111,8 +111,10 @@ ProjectDock::ProjectDock(QWidget *parent)
connect(mProjectView->selectionModel(), &QItemSelectionModel::currentRowChanged,
this, &ProjectDock::onCurrentRowChanged);

connect(mProjectView->model(), &ProjectModel::folderAdded, this, &ProjectDock::folderAdded);
connect(mProjectView->model(), &ProjectModel::folderRemoved, this, &ProjectDock::folderRemoved);
// Forwarding signals
auto projectModel = mProjectView->projectModel();
connect(projectModel, &ProjectModel::folderAdded, this, &ProjectDock::folderAdded);
connect(projectModel, &ProjectModel::folderRemoved, this, &ProjectDock::folderRemoved);
}

void ProjectDock::addFolderToProject()
Expand All @@ -134,15 +136,15 @@ void ProjectDock::addFolderToProject()
if (folder.isEmpty())
return;

mProjectView->model()->addFolder(folder);
mProjectView->projectModel()->addFolder(folder);
mProjectView->addExpandedPath(folder);

project.save();
}

void ProjectDock::refreshProjectFolders()
{
mProjectView->model()->refreshFolders();
mProjectView->projectModel()->refreshFolders();
}

void ProjectDock::setExpandedPaths(const QStringList &expandedPaths)
Expand All @@ -167,7 +169,7 @@ void ProjectDock::onCurrentRowChanged(const QModelIndex &current)
if (!current.isValid())
return;

const auto filePath = mProjectView->model()->filePath(current);
const auto filePath = mProjectView->filePath(current);
if (QFileInfo { filePath }.isFile())
emit fileSelected(filePath);
}
Expand All @@ -193,27 +195,29 @@ ProjectView::ProjectView(QWidget *parent)
setDefaultDropAction(Qt::MoveAction);
setDragDropMode(QAbstractItemView::DragOnly);

auto model = ProjectManager::instance()->projectModel();
setModel(model);
mProjectModel = ProjectManager::instance()->projectModel();
mProxyModel = new ProjectProxyModel(this);
mProxyModel->setSourceModel(mProjectModel);
setModel(mProxyModel);

connect(this, &QAbstractItemView::activated,
this, &ProjectView::onActivated);

connect(model, &QAbstractItemModel::rowsInserted,
connect(mProxyModel, &QAbstractItemModel::rowsInserted,
this, &ProjectView::onRowsInserted);

connect(this, &QTreeView::expanded,
this, [=] (const QModelIndex &index) { mExpandedPaths.insert(model->filePath(index)); });
this, [=] (const QModelIndex &index) { mExpandedPaths.insert(filePath(index)); });
connect(this, &QTreeView::collapsed,
this, [=] (const QModelIndex &index) { mExpandedPaths.remove(model->filePath(index)); });
this, [=] (const QModelIndex &index) { mExpandedPaths.remove(filePath(index)); });

// Reselect a previously selected path and restore scrollbar after refresh
connect(model, &ProjectModel::aboutToRefresh,
connect(mProjectModel, &ProjectModel::aboutToRefresh,
this, [=] {
mSelectedPath = model->filePath(currentIndex());
mSelectedPath = filePath(currentIndex());
mScrollBarValue = verticalScrollBar()->value();
});
connect(model, &ProjectModel::refreshed,
connect(mProjectModel, &ProjectModel::refreshed,
this, [=] {
selectPath(mSelectedPath);
verticalScrollBar()->setValue(mScrollBarValue);
Expand All @@ -225,13 +229,6 @@ QSize ProjectView::sizeHint() const
return Utils::dpiScaled(QSize(250, 200));
}

void ProjectView::setModel(QAbstractItemModel *model)
{
mProjectModel = qobject_cast<ProjectModel*>(model);
Q_ASSERT(mProjectModel);
QTreeView::setModel(model);
}

void ProjectView::setExpandedPaths(const QStringList &paths)
{
mExpandedPaths = QSet<QString>(paths.begin(), paths.end());
Expand All @@ -244,38 +241,44 @@ void ProjectView::addExpandedPath(const QString &path)

void ProjectView::selectPath(const QString &path)
{
auto index = model()->index(path);
if (index.isValid())
setCurrentIndex(index);
const auto sourceIndex = mProjectModel->index(path);
const auto proxyIndex = mProxyModel->mapFromSource(sourceIndex);
if (proxyIndex.isValid())
setCurrentIndex(proxyIndex);
}

QString ProjectView::filePath(const QModelIndex &index) const
{
return mProjectModel->filePath(mProxyModel->mapToSource(index));
}

void ProjectView::contextMenuEvent(QContextMenuEvent *event)
{
const QModelIndex index = indexAt(event->pos());
const auto index = indexAt(event->pos());

QMenu menu;

if (index.isValid()) {
const auto filePath = model()->filePath(index);
const auto path = filePath(index);

Utils::addFileManagerActions(menu, filePath);
Utils::addFileManagerActions(menu, path);

if (QFileInfo { filePath }.isFile()) {
Utils::addOpenWithSystemEditorAction(menu, filePath);
if (QFileInfo { path }.isFile()) {
Utils::addOpenWithSystemEditorAction(menu, path);

auto mapDocumentActionHandler = MapDocumentActionHandler::instance();
auto mapDocument = mapDocumentActionHandler->mapDocument();

// Add template-specific actions
auto objectTemplate = TemplateManager::instance()->loadObjectTemplate(filePath);
auto objectTemplate = TemplateManager::instance()->loadObjectTemplate(path);
if (objectTemplate->object()) {
menu.addSeparator();
menu.addAction(tr("Select Template Instances"), [=] {
mapDocumentActionHandler->selectAllInstances(objectTemplate);
})->setEnabled(mapDocument != nullptr);
}
// Add tileset-specific actions
else if (auto tileset = TilesetManager::instance()->loadTileset(filePath)) {
else if (auto tileset = TilesetManager::instance()->loadTileset(path)) {
if (mapDocument) {
auto documentManager = DocumentManager::instance();
auto mapEditor = static_cast<MapEditor*>(documentManager->editor(Document::MapDocumentType));
Expand All @@ -301,8 +304,8 @@ void ProjectView::contextMenuEvent(QContextMenuEvent *event)
if (!index.parent().isValid()) {
menu.addSeparator();
auto removeFolder = menu.addAction(tr("&Remove Folder from Project"), [=] {
model()->removeFolder(index.row());
model()->project().save();
projectModel()->removeFolder(index.row());
projectModel()->project().save();
});
Utils::setThemeIcon(removeFolder, "list-remove");
}
Expand All @@ -319,7 +322,7 @@ void ProjectView::contextMenuEvent(QContextMenuEvent *event)

void ProjectView::onActivated(const QModelIndex &index)
{
const QString path = model()->filePath(index);
const QString path = filePath(index);
if (QFileInfo(path).isFile())
DocumentManager::instance()->openFile(path);
}
Expand All @@ -332,7 +335,7 @@ void ProjectView::onRowsInserted(const QModelIndex &parent)

void ProjectView::restoreExpanded(const QModelIndex &parent)
{
const QString path = model()->filePath(parent);
const QString path = filePath(parent);

if (mExpandedPaths.contains(path)) {
setExpanded(parent, true);
Expand Down
2 changes: 0 additions & 2 deletions src/tiled/projectdock.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,6 @@

#pragma once

#include "project.h"

#include <QDockWidget>

namespace Tiled {
Expand Down
Loading