Skip to content

Commit

Permalink
Recursive Models and Room Editor Rendering (#36)
Browse files Browse the repository at this point in the history
* Added a 500mb pixmap cache to the `ArtManager` class for caching large images used by the background, sprite, and room renderering widgets. That way they don't each load their own copy. Later it can be expanded with a file change monitor to automatically reload the pixmaps when they are changed on disk.
* Changed ArtManager::GetIcon to load more icons that have not yet been loaded.
* Used `QMenuView` widget by Ulrich Van Den Hekke to implement resource picker.
* Changed `BackgroundEditor` and `ScriptEditor` to use the renamed default mapper of `BaseEditor` which distinguishes it from the mapper used for mapping the resource's name in the tree model.
* Changed `BackgroundEditor::dataChanged` to forward itself to the `BaseEditor` base event.
* Made the `BackgroundEditor` save button mutate the dirty state of the default resource mapper and not the dirty state of its model.
* Changed the `BaseEditor` close event to check the dirty state of the default resource mapper and not the dirty state of its model. When not saving changes it also now clears the mappings to its tree node mapper. Finally, it will otherwise mark its mapper as not dirty now instead of its model.
* Added a map of resource type cases to to tree node field numbers to `BaseEditor.h` to facilitate looking up the submodel of a tree node.
* Added a `ResourceRenamed` signal to `BaseEditor` that is emitted when the tree node of the resource is renamed.
* Used the tree node mapper of `BaseEditor` in the room editor to map the name field on the settings tab.
* Used the resource mapper of `BaseEditor` in the room editor to map the rest of the settings that are fields of the root room resource proto.
* Added a new mapper in the room editor to map the view settings on the views tab.
* Made the current view combo box change the currently mapped view resource on the views tab.
* Tied the selection of assets in the assets list to the model shown in the asset properties view. Later this same selection model needs to be shared with the RoomRenderer as well.
* Added a status bar just under the room renderer that tracks the mouse position.
* Used an event filter to paint the transparency checkerboard pattern on the background of the room preview scroll area viewport.
* Added zooming to the room editor and room renderer.
* Put the room preview and the room property tabs into a horizontal splitter so more screen space can be used when designing levels.
* Because we decided to deprecate the transparent field from GM6 in libEGM when I fixed GMK, I removed the transparent checkbox from the sprite and background editors as well as the logic from both renderers.
* Changed the close event of the main window to close all the subwindows which triggers their save changes dialog. More work needs to be done to this to make it perfect. Even if you decline the individual dialog for each editor, there should still be a final overruling dialog that's shown if the tree model itself is dirty. That is how GM5 behaved though it could be improved upon with "Yes to All" or "No to All".
* Changed the `BaseEditor` constructor to accept the tree node instead of its subresource so editors can map the name field.
* Created the composite `ModelMapper` class that combines `ImmediateDataWidgetMapper` together with `ProtoModel` using traditional class composition. Fundies argued that since everywhere we use a model, we also want a mapper, it ultimately will save redundant code.
* Changed the `ProtoModel` to deal with recursive submodels from nested protobuf messages.
* Added the `RepeatedProtoModel` class for use with repeated submodels, such as instances that are a repeated field in the Room proto.
* Added the `TreeSortFilterProxyModel` class for filtering the main tree by resource type. It is used in conjunction with QMenuView to implement the resource picker which needs to filter by type.
* Added the `RoomRenderer` widget that uses the submodels of the Room proto to render the room layers, views, assets, grid, etc.
* Added the `ResourceModelMap` class that facilitates in finding resources by name as well as getting an object/sprite preview for the tree.
* Fixed a typo in the background renderer's grid painting algorithm. The mistake was that fundies put width where he meant height for the number of horizontal lines.
  • Loading branch information
RemoveRusky authored and RobertBColton committed Oct 29, 2018
1 parent fbe07cc commit 9448192
Show file tree
Hide file tree
Showing 36 changed files with 2,595 additions and 1,309 deletions.
12 changes: 12 additions & 0 deletions CmakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,19 @@ set(RGM_SOURCES
Models/TreeModel.cpp
Models/SpriteModel.cpp
Models/ProtoModel.cpp
Models/ModelMapper.cpp
Models/ImmediateMapper.cpp
Models/ResourceModelMap.cpp
Models/RepeatedProtoModel.cpp
Models/TreeSortFilterProxyModel.cpp
Components/ArtManager.cpp
Components/Utility.cpp
Components/RecentFiles.cpp
Components/QMenuView.cpp
Widgets/BackgroundRenderer.cpp
Widgets/CodeWidget.cpp
Widgets/ColorPicker.cpp
Widgets/RoomRenderer.cpp
Plugins/RGMPlugin.cpp
Plugins/ServerPlugin.cpp
qtplugins.cpp
Expand All @@ -70,14 +76,20 @@ set(RGM_HEADERS
Editors/SpriteEditor.h
Models/TreeModel.h
Models/ProtoModel.h
Models/ModelMapper.h
Models/ImmediateMapper.h
Models/SpriteModel.h
Models/ResourceModelMap.h
Models/RepeatedProtoModel.h
Models/TreeSortFilterProxyModel.h
Components/ArtManager.h
Components/RecentFiles.h
Components/Utility.h
Components/QMenuView.h
Widgets/BackgroundRenderer.h
Widgets/CodeWidget.h
Widgets/ColorPicker.h
Widgets/RoomRenderer.h
Plugins/RGMPlugin.h
Plugins/ServerPlugin.h
)
Expand Down
20 changes: 19 additions & 1 deletion Components/ArtManager.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include "ArtManager.h"

#include <QDirIterator>
#include <QPixmapCache>

QHash<QString, QIcon> ArtManager::icons;
QBrush ArtManager::transparenyBrush;
Expand All @@ -14,10 +15,27 @@ void ArtManager::Init() {
}

transparenyBrush = QBrush(Qt::black, QPixmap(":/transparent.png"));

QPixmapCache::setCacheLimit(500000); // 500mb cache limit
}

ArtManager::ArtManager() {}

const QIcon& ArtManager::GetIcon(const QString& name) { return icons[name]; }
const QIcon& ArtManager::GetIcon(const QString& name) {
if (!icons.contains(name)) icons[name] = QIcon(name);

return icons[name];
}

const QBrush& ArtManager::GetTransparenyBrush() { return transparenyBrush; }

const QPixmap& ArtManager::GetCachedPixmap(const QString& name) {
QPixmap pm;
if (!QPixmapCache::find(name, &pm)) {
pm.load(name);
QPixmapCache::insert(name, pm);
}
return std::move(pm);
}

void ArtManager::clearCache() { QPixmapCache::clear(); }
2 changes: 2 additions & 0 deletions Components/ArtManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ class ArtManager {
static void Init();
static const QIcon& GetIcon(const QString& name);
static const QBrush& GetTransparenyBrush();
static const QPixmap& GetCachedPixmap(const QString& name);
static void clearCache();

private:
ArtManager();
Expand Down
247 changes: 247 additions & 0 deletions Components/QMenuView.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,247 @@
/*
XINX
Copyright (C) 2007-2011 by Ulrich Van Den Hekke
[email protected]
This program 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 3 of the License, or
(at your option) any later version.
This program 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 <http://www.gnu.org/licenses/>.
*/
/*
* This class is inspired from the Nokia class MenuModel, used in the browser
* application in demos.
*/

// Xinx header
#include "QMenuView_p.h"

Q_DECLARE_METATYPE(QModelIndex);

/* QMenuViewPrivate */

QMenuViewPrivate::QMenuViewPrivate(QMenuView* menu) : _menu(menu)
{
}

QMenuViewPrivate::~QMenuViewPrivate()
{
}


QAction * QMenuViewPrivate::makeAction(const QModelIndex &index)
{
QIcon icon = qvariant_cast<QIcon>(index.data(Qt::DecorationRole));
QAction * action = new QAction(icon, index.data().toString(), this);
action->setEnabled(index.flags().testFlag(Qt::ItemIsEnabled));
// improvements for Qlipper (petr vanek <[email protected]>
action->setFont(qvariant_cast<QFont>(index.data(Qt::FontRole)));
action->setToolTip(index.data(Qt::ToolTipRole).toString());
// end of qlipper improvements
QVariant v;
v.setValue(index);
action->setData(v);

return action;
}


void QMenuViewPrivate::triggered(QAction *action)
{
QVariant v = action->data();
if (v.canConvert<QModelIndex>())
{
QModelIndex idx = qvariant_cast<QModelIndex>(v);
emit _menu->triggered(idx);
}
}

void QMenuViewPrivate::hovered(QAction *action)
{
QVariant v = action->data();
if (v.canConvert<QModelIndex>())
{
QModelIndex idx = qvariant_cast<QModelIndex>(v);
QString hoveredString = idx.data(Qt::StatusTipRole).toString();
if (!hoveredString.isEmpty())
emit _menu->hovered(hoveredString);
}
}

void QMenuViewPrivate::aboutToShow()
{
QMenu * menu = qobject_cast<QMenu*>(sender());
if (menu)
{
QVariant v = menu->menuAction()->data();
if (v.canConvert<QModelIndex>())
{
QModelIndex idx = qvariant_cast<QModelIndex>(v);
_menu->createMenu(idx, menu, menu);
disconnect(menu, SIGNAL(aboutToShow()), this, SLOT(aboutToShow()));
return;
}
}

_menu->clear();

if (_menu->prePopulated())
_menu->addSeparator();

_menu->createMenu(m_root, _menu, _menu);

_menu->postPopulated();
}

/* QMenuView */

/*!
* \ingroup Components
* \class QMenuView
* \since 0.9.0.0
*
* \brief The QMenuView provides a menu based view on a QAbstractItemModel class.
*
* \bc 0.10.0.0
*
* This class is used to transform a hierarchical model based on the class
* QAbstractItemModel into a menu. It can be used to create an action menu, history,
* or snipets menu.
*
* \image html qmenuview.png
* \image latex qmenuview.png
*
* When the model is defined, the structure of the menu is automatically generated. This
* class ignores call to QAbstractItemModel::beginInsertRows() and QAbstractItemModel::endInsertRows().
* Menu is generated when the user opens it.
*/

/*!
* \brief Creates the new menu view based on a QMenu object.
* \param parent The parent object of the menu.
*/
QMenuView::QMenuView(QWidget * parent) : QMenu(parent), d(new QMenuViewPrivate(this))
{
connect(this, SIGNAL(triggered(QAction*)), d.data(), SLOT(triggered(QAction*)));
connect(this, SIGNAL(hovered(QAction*)), d.data(), SLOT(hovered(QAction*)));
connect(this, SIGNAL(aboutToShow()), d.data(), SLOT(aboutToShow()));
}

//! Destroy the menu.
QMenuView::~QMenuView()
{
setModel(0);
}

/*!
* \fn void QMenuView::hovered(const QString &text) const
* \brief The signal when a menu action is highlighted.
*
* \p text is the Qt::StatusTipRole of the index that caused the signal to be emitted.
*
* Often this is used to update status information.
*
* \sa triggered()
*/

/*!
* \fn void QMenuView::triggered(const QModelIndex & index) const
* \brief This signal is emitted when an action in this menu is triggered.
*
* \p index is the index's action that caused the signal to be emitted.
*
* \sa hovered()
*/

//! Add any actions before the tree, return true if any actions are added.
bool QMenuView::prePopulated()
{
return false;
}

//! Add any actions after the tree
void QMenuView::postPopulated()
{
}

/*!
* \brief Set the new model to \p model.
* \param model The new model to use for the creation of menus.
*/
void QMenuView::setModel(QAbstractItemModel * model)
{
d->m_model = model;
}

/*!
* \brief Return the current model of the menu.
*/
QAbstractItemModel * QMenuView::model() const
{
return d->m_model;
}

/*!
* \brief Change the root index to \p index.
*
* This can be used to show only a part of the QAbstractItemModel.
* \param index The index to use to show the menu. if QModelIndex(), all the model is show.
*/
void QMenuView::setRootIndex(const QModelIndex & index)
{
d->m_root = index;
}

/*!
* \brief Returns the current root index.
*
* Default root index is QModelIndex()
*/
QModelIndex QMenuView::rootIndex() const
{
return d->m_root;
}

//! Puts all of the children of parent into menu
void QMenuView::createMenu(const QModelIndex &parent, QMenu *parentMenu, QMenu *menu)
{
if (! menu)
{
QIcon icon = qvariant_cast<QIcon>(parent.data(Qt::DecorationRole));

QVariant v;
v.setValue(parent);

menu = new QMenu(parent.data().toString(), this);
menu->setIcon(icon);
parentMenu->addMenu(menu);
menu->menuAction()->setData(v);
menu->setEnabled(parent.flags().testFlag(Qt::ItemIsEnabled));

connect(menu, SIGNAL(aboutToShow()), d.data(), SLOT(aboutToShow()));

return;
}

int end = d->m_model->rowCount(parent);
for (int i = 0; i < end; ++i)
{
QModelIndex idx = d->m_model->index(i, 0, parent);
if (d->m_model->hasChildren(idx))
{
createMenu(idx, menu);
}
else
{
menu->addAction(d->makeAction(idx));
}
}
}
58 changes: 58 additions & 0 deletions Components/QMenuView.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
XINX
Copyright (C) 2007-2011 by Ulrich Van Den Hekke
[email protected]
This program 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 3 of the License, or
(at your option) any later version.
This program 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 <http://www.gnu.org/licenses/>.
*/

#pragma once
#ifndef _QMENUVIEW_H_
#define _QMENUVIEW_H_

// Qt header
#include <QMenu>
#include <QAbstractItemModel>

//#include <components-config.h>
#define COMPONENTSEXPORT

class QMenuViewPrivate;

class COMPONENTSEXPORT QMenuView : public QMenu
{
Q_OBJECT
public:
QMenuView(QWidget * parent = 0);
virtual ~QMenuView();

virtual void setModel(QAbstractItemModel * model);
QAbstractItemModel * model() const;

virtual void setRootIndex(const QModelIndex & index);
QModelIndex rootIndex() const;
protected:
virtual bool prePopulated();
virtual void postPopulated();
void createMenu(const QModelIndex &parent, QMenu *parentMenu = 0, QMenu *menu = 0);
signals:
void hovered(const QString &text) const;
void triggered(const QModelIndex & index) const;
private:
QScopedPointer<QMenuViewPrivate> d;
friend class QMenuViewPrivate;
};


#endif /* _QMENUVIEW_H_ */
Loading

0 comments on commit 9448192

Please sign in to comment.