diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..c61f542 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "rust-qt-binding-generator"] + path = rust-qt-binding-generator + url = https://git.greyserv.net/marrub/rust-qt-binding-generator diff --git a/rust-qt-binding-generator b/rust-qt-binding-generator new file mode 160000 index 0000000..598336e --- /dev/null +++ b/rust-qt-binding-generator @@ -0,0 +1 @@ +Subproject commit 598336e657cb4bf5ef5070dfa35b999b7dfd341b diff --git a/tycho/CMakeLists.txt b/tycho/CMakeLists.txt index 91c34c2..a5f7119 100644 --- a/tycho/CMakeLists.txt +++ b/tycho/CMakeLists.txt @@ -23,6 +23,7 @@ add_library( SHARED $ENV{OUT_DIR}/bindings.cc $ENV{OUT_DIR}/bindings.h + cc/interface.h cc/main.cc cc/mapmodel.cc cc/mapprops.cc @@ -51,6 +52,7 @@ target_include_directories( maraiah-tycho-hermes PUBLIC $ENV{OUT_DIR} + cc ) target_link_libraries( diff --git a/tycho/Cargo.toml b/tycho/Cargo.toml index 7714924..3453c8a 100644 --- a/tycho/Cargo.toml +++ b/tycho/Cargo.toml @@ -10,7 +10,7 @@ maraiah = {path = ".."} [build-dependencies] cmake = "0.1" maraiah = {path = ".."} -rust_qt_binding_generator = "0.3" +rust_qt_binding_generator = {path = "../rust-qt-binding-generator"} [[bin]] name = "tycho" diff --git a/tycho/bindings.json b/tycho/bindings.json index 0280840..6c956b8 100644 --- a/tycho/bindings.json +++ b/tycho/bindings.json @@ -1,4 +1,5 @@ { + "beforeHeader": "interface.h", "cppFile": "", "rust": { "dir": "", @@ -8,6 +9,7 @@ "objects": { "IMapModel": { "type": "List", + "baseClass": "IProjectModel", "functions": { "open": { "return": "bool", @@ -27,10 +29,6 @@ {"name": "path", "type": "QString"} ] }, - "isDirty": { - "return": "bool", - "mut": false - }, "propIcon": { "return": "QString", "mut": false, @@ -51,10 +49,20 @@ } }, "properties": { + "dirty": {"type": "bool", "write": true} }, "itemProperties": { "propIndex": {"type": "quint64", "roles": [["display"]]} } + }, + "IMapView": { + "type": "Object", + "functions": { + }, + "properties": { + }, + "itemProperties": { + } } } } diff --git a/tycho/cc/interface.h b/tycho/cc/interface.h new file mode 100644 index 0000000..5f97b0e --- /dev/null +++ b/tycho/cc/interface.h @@ -0,0 +1,40 @@ +#pragma once + +#pragma clang diagnostic ignored "-Winconsistent-missing-override" + +#include + +class IProjectModel : public QAbstractItemModel +{ + Q_OBJECT + Q_PROPERTY(bool dirty READ dirty WRITE setDirty NOTIFY dirtyChanged) + +public: + using QAbstractItemModel::QAbstractItemModel; + + virtual ~IProjectModel() {} + + virtual bool dirty() const = 0; + virtual void setDirty(bool dirty) = 0; + + virtual bool open(QString const &path) = 0; + virtual bool save() const = 0; + virtual bool saveAs(QString const &path) const = 0; + +public slots: + virtual void deselect() = 0; + virtual void select(QModelIndex const &index) = 0; + +signals: + void dirtyChanged(bool dirty); + void deselected(); + void selected(std::uint16_t index); +}; + +class IProjectView +{ +public: + virtual ~IProjectView() {} +}; + +// EOF diff --git a/tycho/cc/mapmodel.cc b/tycho/cc/mapmodel.cc index 0ef3596..7424c83 100644 --- a/tycho/cc/mapmodel.cc +++ b/tycho/cc/mapmodel.cc @@ -1,45 +1,24 @@ #include "tycho.h" -CMapModel::CMapModel(Project *parent) : - IMapModel(static_cast(parent)), - IProjectModel() +MapModel::MapModel(Project *parent) : + IMapModel(parent) { dbgPrintFunc(); } -CMapModel::~CMapModel() +MapModel::~MapModel() { dbgPrintFunc(); } -bool CMapModel::isDirty() const -{ - return IMapModel::isDirty(); -} - -bool CMapModel::open(QString const &path) -{ - return IMapModel::open(path); -} - -bool CMapModel::save() const -{ - return IMapModel::save(); -} - -bool CMapModel::saveAs(QString const &path) const -{ - return IMapModel::saveAs(path); -} - -void CMapModel::deselect() +void MapModel::deselect() { IMapModel::deselect(); emit deselected(); } -void CMapModel::select(QModelIndex const &index) +void MapModel::select(QModelIndex const &index) { auto idx = index.internalId(); @@ -48,7 +27,7 @@ void CMapModel::select(QModelIndex const &index) emit selected(idx); } -QVariant CMapModel::data(const QModelIndex &index, int role) const +QVariant MapModel::data(const QModelIndex &index, int role) const { switch(role) { case Qt::DecorationRole: { diff --git a/tycho/cc/mapprops.cc b/tycho/cc/mapprops.cc index 992a938..823a97f 100644 --- a/tycho/cc/mapprops.cc +++ b/tycho/cc/mapprops.cc @@ -2,7 +2,7 @@ MapProps::MapProps(Project *parent) : QDialog(static_cast(parent)), - mapModel(parent->getMapModel()) + m_mapModel(parent->mapModel()) { setupUi(this); diff --git a/tycho/cc/mapview.cc b/tycho/cc/mapview.cc index 051df96..1a336e0 100644 --- a/tycho/cc/mapview.cc +++ b/tycho/cc/mapview.cc @@ -1,15 +1,15 @@ #include "tycho.h" -CMapView::CMapView(Project *parent) : +MapView::MapView(Project *parent) : QWidget(static_cast(parent)), - mapModel(parent->getMapModel()) + m_mapModel(parent->mapModel()) { setupUi(this); dbgPrintFunc(); } -CMapView::~CMapView() +MapView::~MapView() { dbgPrintFunc(); } diff --git a/tycho/cc/menu.cc b/tycho/cc/menu.cc index 22ec6a5..f3a2f42 100644 --- a/tycho/cc/menu.cc +++ b/tycho/cc/menu.cc @@ -22,7 +22,7 @@ Menu::~Menu() void Menu::mapNew() { - QScopedPointer proj{new Project(ProjectType::Map)}; + QScopedPointer proj{new Project(Project::Map)}; addProject(proj.take()); } @@ -40,9 +40,9 @@ void Menu::mapOpen() "All files (*)")); if(!fname.isEmpty()) { - QScopedPointer proj{new Project(ProjectType::Map)}; + QScopedPointer proj{new Project(Project::Map)}; - if(proj->getModel()->open(fname)) { + if(proj->model()->open(fname)) { addProject(proj.take()); } } @@ -98,7 +98,7 @@ void Menu::openMapProperties() { auto proj = activeProject(); - if(proj && proj->getType() == ProjectType::Map) { + if(proj && proj->type() == Project::Map) { MapProps props{proj}; props.exec(); } @@ -106,14 +106,14 @@ void Menu::openMapProperties() void Menu::updateActions() { - std::optional active; + std::optional active; if(auto proj = activeProject()) { - active = proj->getType(); + active = proj->type(); } actionClose->setEnabled(!!active); - actionMapProps->setEnabled(active == ProjectType::Map); + actionMapProps->setEnabled(active == Project::Map); } void Menu::closeEvent(QCloseEvent *event) diff --git a/tycho/cc/project.cc b/tycho/cc/project.cc index adb470f..a46b8c2 100644 --- a/tycho/cc/project.cc +++ b/tycho/cc/project.cc @@ -3,24 +3,24 @@ static IProjectModel *makeModel(Project *proj) { - switch(proj->getType()) { - case ProjectType::Map: return new MapModel(proj); + switch(proj->type()) { + case Project::Map: return new MapModel(proj); } } static IProjectView *makeView(Project *proj) { - switch(proj->getType()) { - case ProjectType::Map: return new MapView(proj); + switch(proj->type()) { + case Project::Map: return new MapView(proj); } } -Project::Project(ProjectType _type) : +Project::Project(Type type) : QMdiSubWindow(), - type(_type), - model(makeModel(this)), - view(makeView(this)) + m_type(type), + m_model(makeModel(this)), + m_view(makeView(this)) { auto widget = new QWidget(this); @@ -29,12 +29,12 @@ Project::Project(ProjectType _type) : setWidget(widget); setAttribute(Qt::WA_DeleteOnClose); - listView->setModel(dynamic_cast(model)); - verticalLayout->insertWidget(0, dynamic_cast(view)); + listView->setModel(m_model); + verticalLayout->insertWidget(0, dynamic_cast(m_view)); connect(listView, SIGNAL(doubleClicked(QModelIndex const &)), - dynamic_cast(model), + m_model, SLOT(select(QModelIndex const &))); dbgPrintFunc(); @@ -45,24 +45,24 @@ Project::~Project() dbgPrintFunc(); } -ProjectType Project::getType() const +Project::Type Project::type() const { - return type; + return m_type; } -ProjectModel *Project::getModel() const +IProjectModel *Project::model() const { - return model; + return m_model; } -MapModel *Project::getMapModel() const +MapModel *Project::mapModel() const { - return dynamic_cast(model); + return dynamic_cast(m_model); } void Project::closeEvent(QCloseEvent *event) { - if(model->isDirty()) { + if(m_model->dirty()) { QMessageBox msg; msg.setText(tr("Do you want to save your changes to this project before closing it?")); msg.setInformativeText(tr("Unsaved changes will be lost unless you save.")); @@ -73,7 +73,7 @@ void Project::closeEvent(QCloseEvent *event) switch(msg.exec()) { case QMessageBox::Save: - model->save(); + m_model->save(); break; case QMessageBox::Discard: break; diff --git a/tycho/cc/tycho.h b/tycho/cc/tycho.h index 630ec80..49976b5 100644 --- a/tycho/cc/tycho.h +++ b/tycho/cc/tycho.h @@ -16,8 +16,6 @@ #include #include -#include "bindings.h" - #include "../ui/ui_about.h" #include "../ui/ui_license.h" #include "../ui/ui_mapprops.h" @@ -25,69 +23,42 @@ #include "../ui/ui_menu.h" #include "../ui/ui_project.h" -enum class ProjectType -{ - Map, -}; +#include "bindings.h" + +#pragma clang diagnostic warning "-Winconsistent-missing-override" + +class Project; using byte = std::uint8_t; -class IProjectModel -{ -public: - virtual ~IProjectModel() {} - - virtual bool isDirty() const = 0; - virtual bool open(QString const &path) = 0; - virtual bool save() const = 0; - virtual bool saveAs(QString const &path) const = 0; -}; - -class IProjectView -{ -public: - virtual ~IProjectView() {} -}; - -class CMapModel final : public IMapModel, - public IProjectModel +class MapModel final : public IMapModel { Q_OBJECT public: - explicit CMapModel(Project *parent); - ~CMapModel() override; - - bool isDirty() const override; - bool open(QString const &path) override; - bool save() const override; - bool saveAs(QString const &path) const override; + explicit MapModel(Project *parent); + ~MapModel() override; public slots: - void deselect(); - void select(QModelIndex const &index); - -signals: - void deselected(); - void selected(std::uint16_t index); + void deselect() override; + void select(QModelIndex const &index) override; private: - QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) - const override; + QVariant data(const QModelIndex &index, int role) const override; }; -class CMapView final : public QWidget, - public IProjectView, - private Ui::CMapView +class MapView final : public QWidget, + public IProjectView, + private Ui::MapView { Q_OBJECT public: - explicit CMapView(Project *parent); - ~CMapView(); + explicit MapView(Project *parent); + ~MapView(); private: - CMapModel *const m_mapModel; + MapModel *const m_mapModel; }; class MapProps final : public QDialog, @@ -102,7 +73,7 @@ public: void accept() override; private: - CMapModel *const m_mapModel; + MapModel *const m_mapModel; }; class Menu final : public QMainWindow, @@ -137,24 +108,28 @@ class Project final : public QMdiSubWindow, private Ui::Project { Q_OBJECT + Q_PROPERTY(Type type READ type CONSTANT) + Q_PROPERTY(IProjectModel *model READ model CONSTANT) + Q_PROPERTY(MapModel *mapModel READ mapModel CONSTANT) public: - explicit Project(ProjectType type); + enum Type {Map}; + Q_ENUM(Type) + + explicit Project(Type type); ~Project(); - ProjectType getType() const; - - ProjectModel *getModel() const; - MapModel *getMapModel() const; + Type type() const; + IProjectModel *model() const; + MapModel *mapModel() const; protected: void closeEvent(QCloseEvent *event) override; private: - ProjectType const type; - - ProjectModel *const model; - ProjectView *const view; + Type const m_type; + IProjectModel *const m_model; + IProjectView *const m_view; }; #pragma clang diagnostic push diff --git a/tycho/source/gui.rs b/tycho/source/gui.rs index 2c5078c..3a0fef2 100644 --- a/tycho/source/gui.rs +++ b/tycho/source/gui.rs @@ -1,8 +1,10 @@ //! GUI implementation. mod mapmodel; +mod mapview; mod qobj; pub use self::mapmodel::IMapModel; +pub use self::mapview::IMapView; // EOF diff --git a/tycho/source/gui/mapmodel.rs b/tycho/source/gui/mapmodel.rs index 8e1fc51..3cb7b39 100644 --- a/tycho/source/gui/mapmodel.rs +++ b/tycho/source/gui/mapmodel.rs @@ -38,7 +38,7 @@ impl IMapModelTrait for IMapModel eprintln!("new IMapModel"); } - Self{emit, map: EntryDataMap::default(), selected: None} + Self{emit, map: EntryDataMap::default(), selected: None, dirty: false} } /// Returns the emitter of `self`. @@ -99,7 +99,9 @@ impl IMapModelTrait for IMapModel } /// Returns `true` if the file has been modified from its original state. - fn is_dirty(&self) -> bool {false} + fn dirty(&self) -> bool {self.dirty} + + fn set_dirty(&mut self, dirty: bool) {self.dirty = dirty;} fn deselect(&mut self) {self.selected = None;} @@ -127,6 +129,7 @@ pub struct IMapModel { emit: IMapModelEmitter, map: EntryDataMap, selected: Option, + dirty: bool, } // EOF diff --git a/tycho/source/gui/mapview.rs b/tycho/source/gui/mapview.rs new file mode 100644 index 0000000..07255de --- /dev/null +++ b/tycho/source/gui/mapview.rs @@ -0,0 +1,35 @@ +//! Map view. + +use super::qobj::*; + +impl IMapViewTrait for IMapView +{ + /// Returns a new `IMapView` instance. + fn new(emit: IMapViewEmitter) -> Self + { + if cfg!(debug_assertions) { + eprintln!("new IMapView"); + } + + Self{emit} + } + + /// Returns the emitter of `self`. + fn emit(&mut self) -> &mut IMapViewEmitter {&mut self.emit} +} + +impl Drop for IMapView +{ + fn drop(&mut self) + { + if cfg!(debug_assertions) { + eprintln!("drop IMapView"); + } + } +} + +pub struct IMapView { + emit: IMapViewEmitter, +} + +// EOF