mirror of
				https://github.com/ciphervance/supercell-wx.git
				synced 2025-10-31 20:00:05 +00:00 
			
		
		
		
	Initial layer model for layer manager
This commit is contained in:
		
							parent
							
								
									ad55ec7f51
								
							
						
					
					
						commit
						f0822205a4
					
				
					 6 changed files with 504 additions and 3 deletions
				
			
		
							
								
								
									
										393
									
								
								scwx-qt/source/scwx/qt/model/layer_model.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										393
									
								
								scwx-qt/source/scwx/qt/model/layer_model.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,393 @@ | |||
| #include <scwx/qt/model/layer_model.hpp> | ||||
| #include <scwx/qt/manager/placefile_manager.hpp> | ||||
| #include <scwx/qt/types/qt_types.hpp> | ||||
| #include <scwx/util/logger.hpp> | ||||
| 
 | ||||
| #include <variant> | ||||
| 
 | ||||
| #include <QApplication> | ||||
| #include <QCheckBox> | ||||
| #include <QFontMetrics> | ||||
| #include <QStyle> | ||||
| #include <QStyleOption> | ||||
| 
 | ||||
| namespace scwx | ||||
| { | ||||
| namespace qt | ||||
| { | ||||
| namespace model | ||||
| { | ||||
| 
 | ||||
| static const std::string logPrefix_ = "scwx::qt::model::layer_model"; | ||||
| static const auto        logger_    = scwx::util::Logger::Create(logPrefix_); | ||||
| 
 | ||||
| static constexpr int kFirstColumn = static_cast<int>(LayerModel::Column::Order); | ||||
| static constexpr int kLastColumn = | ||||
|    static_cast<int>(LayerModel::Column::Description); | ||||
| static constexpr int kNumColumns = kLastColumn - kFirstColumn + 1; | ||||
| 
 | ||||
| static const std::unordered_map<LayerModel::LayerType, std::string> | ||||
|    layerTypeNames_ {{LayerModel::LayerType::Map, "Map"}, | ||||
|                     {LayerModel::LayerType::Radar, "Radar"}, | ||||
|                     {LayerModel::LayerType::Alert, "Alert"}, | ||||
|                     {LayerModel::LayerType::Placefile, "Placefile"}}; | ||||
| 
 | ||||
| class LayerModelImpl | ||||
| { | ||||
| public: | ||||
|    explicit LayerModelImpl() {} | ||||
|    ~LayerModelImpl() = default; | ||||
| 
 | ||||
|    std::shared_ptr<manager::PlacefileManager> placefileManager_ { | ||||
|       manager::PlacefileManager::Instance()}; | ||||
| 
 | ||||
|    std::vector<std::pair<LayerModel::LayerType, std::variant<std::string>>> | ||||
|       layers_ {}; | ||||
| }; | ||||
| 
 | ||||
| LayerModel::LayerModel(QObject* parent) : | ||||
|     QAbstractTableModel(parent), p(std::make_unique<LayerModelImpl>()) | ||||
| { | ||||
|    connect(p->placefileManager_.get(), | ||||
|            &manager::PlacefileManager::PlacefileEnabled, | ||||
|            this, | ||||
|            &LayerModel::HandlePlacefileUpdate); | ||||
| 
 | ||||
|    connect(p->placefileManager_.get(), | ||||
|            &manager::PlacefileManager::PlacefileRemoved, | ||||
|            this, | ||||
|            &LayerModel::HandlePlacefileRemoved); | ||||
| 
 | ||||
|    connect(p->placefileManager_.get(), | ||||
|            &manager::PlacefileManager::PlacefileRenamed, | ||||
|            this, | ||||
|            &LayerModel::HandlePlacefileRenamed); | ||||
| 
 | ||||
|    connect(p->placefileManager_.get(), | ||||
|            &manager::PlacefileManager::PlacefileUpdated, | ||||
|            this, | ||||
|            &LayerModel::HandlePlacefileUpdate); | ||||
| } | ||||
| LayerModel::~LayerModel() = default; | ||||
| 
 | ||||
| int LayerModel::rowCount(const QModelIndex& parent) const | ||||
| { | ||||
|    return parent.isValid() ? 0 : static_cast<int>(p->layers_.size()); | ||||
| } | ||||
| 
 | ||||
| int LayerModel::columnCount(const QModelIndex& parent) const | ||||
| { | ||||
|    return parent.isValid() ? 0 : kNumColumns; | ||||
| } | ||||
| 
 | ||||
| Qt::ItemFlags LayerModel::flags(const QModelIndex& index) const | ||||
| { | ||||
|    Qt::ItemFlags flags = QAbstractTableModel::flags(index); | ||||
| 
 | ||||
|    switch (index.column()) | ||||
|    { | ||||
|    case static_cast<int>(Column::EnabledMap1): | ||||
|    case static_cast<int>(Column::EnabledMap2): | ||||
|    case static_cast<int>(Column::EnabledMap3): | ||||
|    case static_cast<int>(Column::EnabledMap4): | ||||
|       flags |= Qt::ItemFlag::ItemIsUserCheckable | Qt::ItemFlag::ItemIsEditable; | ||||
|       break; | ||||
| 
 | ||||
|    default: | ||||
|       break; | ||||
|    } | ||||
| 
 | ||||
|    return flags; | ||||
| } | ||||
| 
 | ||||
| QVariant LayerModel::data(const QModelIndex& index, int role) const | ||||
| { | ||||
|    static const QString enabledString  = QObject::tr("Enabled"); | ||||
|    static const QString disabledString = QObject::tr("Disabled"); | ||||
| 
 | ||||
|    if (!index.isValid() || index.row() < 0 || | ||||
|        static_cast<std::size_t>(index.row()) >= p->layers_.size()) | ||||
|    { | ||||
|       return QVariant(); | ||||
|    } | ||||
| 
 | ||||
|    const auto& layer   = p->layers_.at(index.row()); | ||||
|    bool        enabled = true; // TODO
 | ||||
| 
 | ||||
|    switch (index.column()) | ||||
|    { | ||||
|    case static_cast<int>(Column::Order): | ||||
|       if (role == Qt::ItemDataRole::DisplayRole) | ||||
|       { | ||||
|          return index.row(); | ||||
|       } | ||||
|       break; | ||||
| 
 | ||||
|    case static_cast<int>(Column::EnabledMap1): | ||||
|    case static_cast<int>(Column::EnabledMap2): | ||||
|    case static_cast<int>(Column::EnabledMap3): | ||||
|    case static_cast<int>(Column::EnabledMap4): | ||||
|       // TODO
 | ||||
|       if (role == Qt::ItemDataRole::ToolTipRole) | ||||
|       { | ||||
|          return enabled ? enabledString : disabledString; | ||||
|       } | ||||
|       else if (role == Qt::ItemDataRole::CheckStateRole) | ||||
|       { | ||||
|          return static_cast<int>(enabled ? Qt::CheckState::Checked : | ||||
|                                            Qt::CheckState::Unchecked); | ||||
|       } | ||||
|       break; | ||||
| 
 | ||||
|    case static_cast<int>(Column::Type): | ||||
|       if (role == Qt::ItemDataRole::DisplayRole || | ||||
|           role == Qt::ItemDataRole::ToolTipRole) | ||||
|       { | ||||
|          return QString::fromStdString(layerTypeNames_.at(layer.first)); | ||||
|       } | ||||
|       break; | ||||
| 
 | ||||
|    case static_cast<int>(Column::Description): | ||||
|       if (role == Qt::ItemDataRole::DisplayRole || | ||||
|           role == Qt::ItemDataRole::ToolTipRole) | ||||
|       { | ||||
|          if (layer.first == LayerType::Placefile) | ||||
|          { | ||||
|             std::string placefileName = std::get<std::string>(layer.second); | ||||
|             std::string description   = placefileName; | ||||
|             std::string title = | ||||
|                p->placefileManager_->placefile_title(placefileName); | ||||
|             if (!title.empty()) | ||||
|             { | ||||
|                description = title + '\n' + description; | ||||
|             } | ||||
| 
 | ||||
|             return QString::fromStdString(description); | ||||
|          } | ||||
|          else | ||||
|          { | ||||
|             if (std::holds_alternative<std::string>(layer.second)) | ||||
|             { | ||||
|                return QString::fromStdString( | ||||
|                   std::get<std::string>(layer.second)); | ||||
|             } | ||||
|          } | ||||
|       } | ||||
|       break; | ||||
| 
 | ||||
|    default: | ||||
|       break; | ||||
|    } | ||||
| 
 | ||||
|    return QVariant(); | ||||
| } | ||||
| 
 | ||||
| QVariant | ||||
| LayerModel::headerData(int section, Qt::Orientation orientation, int role) const | ||||
| { | ||||
|    if (role == Qt::ItemDataRole::DisplayRole) | ||||
|    { | ||||
|       if (orientation == Qt::Horizontal) | ||||
|       { | ||||
|          switch (section) | ||||
|          { | ||||
|          case static_cast<int>(Column::EnabledMap1): | ||||
|             return tr("1"); | ||||
| 
 | ||||
|          case static_cast<int>(Column::EnabledMap2): | ||||
|             return tr("2"); | ||||
| 
 | ||||
|          case static_cast<int>(Column::EnabledMap3): | ||||
|             return tr("3"); | ||||
| 
 | ||||
|          case static_cast<int>(Column::EnabledMap4): | ||||
|             return tr("4"); | ||||
| 
 | ||||
|          case static_cast<int>(Column::Type): | ||||
|             return tr("Type"); | ||||
| 
 | ||||
|          case static_cast<int>(Column::Description): | ||||
|             return tr("Description"); | ||||
| 
 | ||||
|          default: | ||||
|             break; | ||||
|          } | ||||
|       } | ||||
|    } | ||||
|    else if (role == Qt::ItemDataRole::ToolTipRole) | ||||
|    { | ||||
|       switch (section) | ||||
|       { | ||||
|       case static_cast<int>(Column::Order): | ||||
|          return tr("Order"); | ||||
| 
 | ||||
|       case static_cast<int>(Column::EnabledMap1): | ||||
|          return tr("Enabled on Map 1"); | ||||
| 
 | ||||
|       case static_cast<int>(Column::EnabledMap2): | ||||
|          return tr("Enabled on Map 2"); | ||||
| 
 | ||||
|       case static_cast<int>(Column::EnabledMap3): | ||||
|          return tr("Enabled on Map 3"); | ||||
| 
 | ||||
|       case static_cast<int>(Column::EnabledMap4): | ||||
|          return tr("Enabled on Map 4"); | ||||
| 
 | ||||
|       default: | ||||
|          break; | ||||
|       } | ||||
|    } | ||||
|    else if (role == Qt::ItemDataRole::SizeHintRole) | ||||
|    { | ||||
|       switch (section) | ||||
|       { | ||||
|       case static_cast<int>(Column::EnabledMap1): | ||||
|       case static_cast<int>(Column::EnabledMap2): | ||||
|       case static_cast<int>(Column::EnabledMap3): | ||||
|       case static_cast<int>(Column::EnabledMap4): | ||||
|       { | ||||
|          static const QCheckBox checkBox {}; | ||||
|          QStyleOptionButton     option {}; | ||||
|          option.initFrom(&checkBox); | ||||
| 
 | ||||
|          // Width values from QCheckBox
 | ||||
|          return QApplication::style()->sizeFromContents( | ||||
|             QStyle::ContentsType::CT_CheckBox, | ||||
|             &option, | ||||
|             {option.iconSize.width() + 4, 0}); | ||||
|       } | ||||
| 
 | ||||
|       default: | ||||
|          break; | ||||
|       } | ||||
|    } | ||||
| 
 | ||||
|    return QVariant(); | ||||
| } | ||||
| 
 | ||||
| bool LayerModel::setData(const QModelIndex& index, | ||||
|                          const QVariant&    value, | ||||
|                          int                role) | ||||
| { | ||||
|    if (!index.isValid() || index.row() < 0 || | ||||
|        static_cast<std::size_t>(index.row()) >= p->layers_.size()) | ||||
|    { | ||||
|       return false; | ||||
|    } | ||||
| 
 | ||||
|    const auto& layer  = p->layers_.at(index.row()); | ||||
|    bool        result = false; | ||||
| 
 | ||||
|    switch (index.column()) | ||||
|    { | ||||
|    case static_cast<int>(Column::EnabledMap1): | ||||
|    case static_cast<int>(Column::EnabledMap2): | ||||
|    case static_cast<int>(Column::EnabledMap3): | ||||
|    case static_cast<int>(Column::EnabledMap4): | ||||
|       if (role == Qt::ItemDataRole::CheckStateRole) | ||||
|       { | ||||
|          // TODO
 | ||||
|          Q_UNUSED(layer); | ||||
|          Q_UNUSED(value); | ||||
|       } | ||||
|       break; | ||||
| 
 | ||||
|    default: | ||||
|       break; | ||||
|    } | ||||
| 
 | ||||
|    if (result) | ||||
|    { | ||||
|       Q_EMIT dataChanged(index, index); | ||||
|    } | ||||
| 
 | ||||
|    return result; | ||||
| } | ||||
| 
 | ||||
| void LayerModel::HandlePlacefileRemoved(const std::string& name) | ||||
| { | ||||
|    auto it = std::find_if(p->layers_.begin(), | ||||
|                           p->layers_.end(), | ||||
|                           [&name](const auto& layer) | ||||
|                           { | ||||
|                              return layer.first == LayerType::Placefile && | ||||
|                                     std::get<std::string>(layer.second) == name; | ||||
|                           }); | ||||
| 
 | ||||
|    if (it != p->layers_.end()) | ||||
|    { | ||||
|       // Placefile exists, delete row
 | ||||
|       const int row = std::distance(p->layers_.begin(), it); | ||||
| 
 | ||||
|       beginRemoveRows(QModelIndex(), row, row); | ||||
|       p->layers_.erase(it); | ||||
|       endRemoveRows(); | ||||
|    } | ||||
| } | ||||
| 
 | ||||
| void LayerModel::HandlePlacefileRenamed(const std::string& oldName, | ||||
|                                         const std::string& newName) | ||||
| { | ||||
|    auto it = | ||||
|       std::find_if(p->layers_.begin(), | ||||
|                    p->layers_.end(), | ||||
|                    [&oldName](const auto& layer) | ||||
|                    { | ||||
|                       return layer.first == LayerType::Placefile && | ||||
|                              std::get<std::string>(layer.second) == oldName; | ||||
|                    }); | ||||
| 
 | ||||
|    if (it != p->layers_.end()) | ||||
|    { | ||||
|       // Placefile exists, mark row as updated
 | ||||
|       const int   row         = std::distance(p->layers_.begin(), it); | ||||
|       QModelIndex topLeft     = createIndex(row, kFirstColumn); | ||||
|       QModelIndex bottomRight = createIndex(row, kLastColumn); | ||||
| 
 | ||||
|       // Rename placefile
 | ||||
|       it->second = newName; | ||||
| 
 | ||||
|       Q_EMIT dataChanged(topLeft, bottomRight); | ||||
|    } | ||||
|    else | ||||
|    { | ||||
|       // Placefile is new, append row
 | ||||
|       const int newIndex = static_cast<int>(p->layers_.size()); | ||||
|       beginInsertRows(QModelIndex(), newIndex, newIndex); | ||||
|       p->layers_.push_back({LayerType::Placefile, newName}); | ||||
|       endInsertRows(); | ||||
|    } | ||||
| } | ||||
| 
 | ||||
| void LayerModel::HandlePlacefileUpdate(const std::string& name) | ||||
| { | ||||
|    auto it = std::find_if(p->layers_.begin(), | ||||
|                           p->layers_.end(), | ||||
|                           [&name](const auto& layer) | ||||
|                           { | ||||
|                              return layer.first == LayerType::Placefile && | ||||
|                                     std::get<std::string>(layer.second) == name; | ||||
|                           }); | ||||
| 
 | ||||
|    if (it != p->layers_.end()) | ||||
|    { | ||||
|       // Placefile exists, mark row as updated
 | ||||
|       const int   row         = std::distance(p->layers_.begin(), it); | ||||
|       QModelIndex topLeft     = createIndex(row, kFirstColumn); | ||||
|       QModelIndex bottomRight = createIndex(row, kLastColumn); | ||||
| 
 | ||||
|       Q_EMIT dataChanged(topLeft, bottomRight); | ||||
|    } | ||||
|    else | ||||
|    { | ||||
|       // Placefile is new, append row
 | ||||
|       const int newIndex = static_cast<int>(p->layers_.size()); | ||||
|       beginInsertRows(QModelIndex(), newIndex, newIndex); | ||||
|       p->layers_.push_back({LayerType::Placefile, name}); | ||||
|       endInsertRows(); | ||||
|    } | ||||
| } | ||||
| 
 | ||||
| } // namespace model
 | ||||
| } // namespace qt
 | ||||
| } // namespace scwx
 | ||||
							
								
								
									
										72
									
								
								scwx-qt/source/scwx/qt/model/layer_model.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								scwx-qt/source/scwx/qt/model/layer_model.hpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,72 @@ | |||
| #pragma once | ||||
| 
 | ||||
| #include <scwx/qt/types/text_event_key.hpp> | ||||
| #include <scwx/common/geographic.hpp> | ||||
| 
 | ||||
| #include <memory> | ||||
| 
 | ||||
| #include <QAbstractTableModel> | ||||
| 
 | ||||
| namespace scwx | ||||
| { | ||||
| namespace qt | ||||
| { | ||||
| namespace model | ||||
| { | ||||
| 
 | ||||
| class LayerModelImpl; | ||||
| 
 | ||||
| class LayerModel : public QAbstractTableModel | ||||
| { | ||||
| public: | ||||
|    enum class Column : int | ||||
|    { | ||||
|       Order       = 0, | ||||
|       EnabledMap1 = 1, | ||||
|       EnabledMap2 = 2, | ||||
|       EnabledMap3 = 3, | ||||
|       EnabledMap4 = 4, | ||||
|       Type        = 5, | ||||
|       Description = 6 | ||||
|    }; | ||||
| 
 | ||||
|    enum class LayerType | ||||
|    { | ||||
|       Map, | ||||
|       Radar, | ||||
|       Alert, | ||||
|       Placefile | ||||
|    }; | ||||
| 
 | ||||
|    explicit LayerModel(QObject* parent = nullptr); | ||||
|    ~LayerModel(); | ||||
| 
 | ||||
|    int rowCount(const QModelIndex& parent = QModelIndex()) const override; | ||||
|    int columnCount(const QModelIndex& parent = QModelIndex()) const override; | ||||
| 
 | ||||
|    Qt::ItemFlags flags(const QModelIndex& index) const override; | ||||
| 
 | ||||
|    QVariant data(const QModelIndex& index, | ||||
|                  int                role = Qt::DisplayRole) const override; | ||||
|    QVariant headerData(int             section, | ||||
|                        Qt::Orientation orientation, | ||||
|                        int             role = Qt::DisplayRole) const override; | ||||
| 
 | ||||
|    bool setData(const QModelIndex& index, | ||||
|                 const QVariant&    value, | ||||
|                 int                role = Qt::EditRole) override; | ||||
| 
 | ||||
| public slots: | ||||
|    void HandlePlacefileRemoved(const std::string& name); | ||||
|    void HandlePlacefileRenamed(const std::string& oldName, | ||||
|                                const std::string& newName); | ||||
|    void HandlePlacefileUpdate(const std::string& name); | ||||
| 
 | ||||
| private: | ||||
|    friend class LayerModelImpl; | ||||
|    std::unique_ptr<LayerModelImpl> p; | ||||
| }; | ||||
| 
 | ||||
| } // namespace model
 | ||||
| } // namespace qt
 | ||||
| } // namespace scwx
 | ||||
|  | @ -1,6 +1,7 @@ | |||
| #include "layer_dialog.hpp" | ||||
| #include "ui_layer_dialog.h" | ||||
| 
 | ||||
| #include <scwx/qt/model/layer_model.hpp> | ||||
| #include <scwx/util/logger.hpp> | ||||
| 
 | ||||
| namespace scwx | ||||
|  | @ -16,16 +17,41 @@ static const auto        logger_    = scwx::util::Logger::Create(logPrefix_); | |||
| class LayerDialogImpl | ||||
| { | ||||
| public: | ||||
|    explicit LayerDialogImpl() {} | ||||
|    explicit LayerDialogImpl(LayerDialog* self) : | ||||
|        layerModel_ {new model::LayerModel(self)} | ||||
|    { | ||||
|    } | ||||
|    ~LayerDialogImpl() = default; | ||||
| 
 | ||||
|    model::LayerModel* layerModel_; | ||||
| }; | ||||
| 
 | ||||
| LayerDialog::LayerDialog(QWidget* parent) : | ||||
|     QDialog(parent), | ||||
|     p {std::make_unique<LayerDialogImpl>()}, | ||||
|     p {std::make_unique<LayerDialogImpl>(this)}, | ||||
|     ui(new Ui::LayerDialog) | ||||
| { | ||||
|    ui->setupUi(this); | ||||
| 
 | ||||
|    ui->layerTreeView->setModel(p->layerModel_); | ||||
| 
 | ||||
|    auto layerViewHeader = ui->layerTreeView->header(); | ||||
| 
 | ||||
|    layerViewHeader->setMinimumSectionSize(10); | ||||
| 
 | ||||
|    // Enabled columns have a fixed size (checkbox)
 | ||||
|    layerViewHeader->setSectionResizeMode( | ||||
|       static_cast<int>(model::LayerModel::Column::EnabledMap1), | ||||
|       QHeaderView::ResizeMode::ResizeToContents); | ||||
|    layerViewHeader->setSectionResizeMode( | ||||
|       static_cast<int>(model::LayerModel::Column::EnabledMap2), | ||||
|       QHeaderView::ResizeMode::ResizeToContents); | ||||
|    layerViewHeader->setSectionResizeMode( | ||||
|       static_cast<int>(model::LayerModel::Column::EnabledMap3), | ||||
|       QHeaderView::ResizeMode::ResizeToContents); | ||||
|    layerViewHeader->setSectionResizeMode( | ||||
|       static_cast<int>(model::LayerModel::Column::EnabledMap4), | ||||
|       QHeaderView::ResizeMode::ResizeToContents); | ||||
| } | ||||
| 
 | ||||
| LayerDialog::~LayerDialog() | ||||
|  |  | |||
|  | @ -19,6 +19,7 @@ class LayerDialogImpl; | |||
| class LayerDialog : public QDialog | ||||
| { | ||||
|    Q_OBJECT | ||||
|    Q_DISABLE_COPY_MOVE(LayerDialog) | ||||
| 
 | ||||
| public: | ||||
|    explicit LayerDialog(QWidget* parent = nullptr); | ||||
|  |  | |||
|  | @ -36,7 +36,14 @@ | |||
|        <number>0</number> | ||||
|       </property> | ||||
|       <item> | ||||
|        <widget class="QTreeView" name="layerTreeView"/> | ||||
|        <widget class="QTreeView" name="layerTreeView"> | ||||
|         <property name="alternatingRowColors"> | ||||
|          <bool>true</bool> | ||||
|         </property> | ||||
|         <property name="selectionMode"> | ||||
|          <enum>QAbstractItemView::ContiguousSelection</enum> | ||||
|         </property> | ||||
|        </widget> | ||||
|       </item> | ||||
|       <item> | ||||
|        <widget class="QFrame" name="frame_2"> | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Dan Paulat
						Dan Paulat