aboutsummaryrefslogtreecommitdiff
path: root/launcher/ui/pages/modplatform/ResourceModel.h
blob: 05aa6a94299137ee270ecc03746fbedda76ebcc5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
#pragma once

#include <optional>

#include <QAbstractListModel>

#include "QObjectPtr.h"
#include "BaseInstance.h"

#include "modplatform/ResourceAPI.h"

#include "tasks/ConcurrentTask.h"

class NetJob;
class ResourceAPI;

namespace ModPlatform {
struct IndexedPack;
}

namespace ResourceDownload {

class ResourceModel : public QAbstractListModel {
    Q_OBJECT

    Q_PROPERTY(QString search_term MEMBER m_search_term WRITE setSearchTerm)

   public:
    ResourceModel(BaseInstance const&, ResourceAPI* api);
    ~ResourceModel() override;

    [[nodiscard]] auto data(const QModelIndex&, int role) const -> QVariant override;
    [[nodiscard]] auto roleNames() const -> QHash<int, QByteArray> override;
    bool setData(const QModelIndex& index, const QVariant& value, int role) override;

    [[nodiscard]] virtual auto debugName() const -> QString;
    [[nodiscard]] virtual auto metaEntryBase() const -> QString = 0;

    [[nodiscard]] inline int rowCount(const QModelIndex& parent) const override { return parent.isValid() ? 0 : m_packs.size(); }
    [[nodiscard]] inline int columnCount(const QModelIndex& parent) const override { return parent.isValid() ? 0 : 1; }
    [[nodiscard]] inline auto flags(const QModelIndex& index) const -> Qt::ItemFlags override { return QAbstractListModel::flags(index); }

    [[nodiscard]] bool hasActiveSearchJob() const { return m_current_search_job && m_current_search_job->isRunning(); }
    [[nodiscard]] bool hasActiveInfoJob() const { return m_current_info_job.isRunning(); }
    [[nodiscard]] Task::Ptr activeSearchJob() { return hasActiveSearchJob() ? m_current_search_job : nullptr; }

    [[nodiscard]] auto getSortingMethods() const { return m_api->getSortingMethods(); }

   public slots:
    void fetchMore(const QModelIndex& parent) override;
    // NOTE: Can't use [[nodiscard]] here because of https://bugreports.qt.io/browse/QTBUG-58628 on Qt 5.12
    inline bool canFetchMore(const QModelIndex& parent) const override
    {
        return parent.isValid() ? false : m_search_state == SearchState::CanFetchMore;
    }

    void setSearchTerm(QString term) { m_search_term = term; }

    virtual ResourceAPI::SearchArgs createSearchArguments() = 0;
    virtual ResourceAPI::SearchCallbacks createSearchCallbacks() = 0;

    virtual ResourceAPI::VersionSearchArgs createVersionsArguments(QModelIndex&) = 0;
    virtual ResourceAPI::VersionSearchCallbacks createVersionsCallbacks(QModelIndex&) = 0;

    virtual ResourceAPI::ProjectInfoArgs createInfoArguments(QModelIndex&) = 0;
    virtual ResourceAPI::ProjectInfoCallbacks createInfoCallbacks(QModelIndex&) = 0;

    /** Requests the API for more entries. */
    virtual void search();

    /** Applies any processing / extra requests needed to fully load the specified entry's information. */
    virtual void loadEntry(QModelIndex&);

    /** Schedule a refresh, clearing the current state. */
    void refresh();

    /** Gets the icon at the URL for the given index. If it's not fetched yet, fetch it and update when fisinhed. */
    std::optional<QIcon> getIcon(QModelIndex&, const QUrl&);

   protected:
    /** Resets the model's data. */
    void clearData();

    void runSearchJob(NetJob::Ptr);
    void runInfoJob(Task::Ptr);

    [[nodiscard]] auto getCurrentSortingMethodByIndex() const -> std::optional<ResourceAPI::SortingMethod>;

   protected:
    const BaseInstance& m_base_instance;

    /* Basic search parameters */
    enum class SearchState { None, CanFetchMore, ResetRequested, Finished } m_search_state = SearchState::None;
    int m_next_search_offset = 0;
    QString m_search_term;
    unsigned int m_current_sort_index = 0;

    std::unique_ptr<ResourceAPI> m_api;

    // Job for searching for new entries
    shared_qobject_ptr<NetJob> m_current_search_job;
    // Job for fetching versions and extra info on existing entries
    ConcurrentTask m_current_info_job;

    shared_qobject_ptr<NetJob> m_current_icon_job;
    QSet<QUrl> m_currently_running_icon_actions;
    QSet<QUrl> m_failed_icon_actions;

    QList<ModPlatform::IndexedPack> m_packs;

    // HACK: We need this to prevent callbacks from calling the model after it has already been deleted.
    // This leaks a tiny bit of memory per time the user has opened a resource dialog. How to make this better?
    static QHash<ResourceModel*, bool> s_running_models;

   private:
    /* Default search request callbacks */
    void searchRequestFailed(QString reason, int network_error_code);
    void searchRequestAborted();

   signals:
    void versionListUpdated();
    void projectInfoUpdated();
};

}  // namespace ResourceDownload