1 | // Aseprite |
2 | // Copyright (C) 2020-2022 Igara Studio S.A. |
3 | // Copyright (C) 2001-2018 David Capello |
4 | // |
5 | // This program is distributed under the terms of |
6 | // the End-User License Agreement for Aseprite. |
7 | |
8 | #ifdef HAVE_CONFIG_H |
9 | #include "config.h" |
10 | #endif |
11 | |
12 | #include "app/ui/resources_listbox.h" |
13 | |
14 | #include "app/res/resource.h" |
15 | #include "app/res/resources_loader.h" |
16 | #include "app/ui/skin/skin_theme.h" |
17 | #include "ui/graphics.h" |
18 | #include "ui/message.h" |
19 | #include "ui/paint_event.h" |
20 | #include "ui/size_hint_event.h" |
21 | #include "ui/view.h" |
22 | |
23 | namespace app { |
24 | |
25 | using namespace ui; |
26 | using namespace skin; |
27 | |
28 | ////////////////////////////////////////////////////////////////////// |
29 | // ResourceListItem |
30 | |
31 | ResourceListItem::ResourceListItem(Resource* resource) |
32 | : ListItem(resource->id()), m_resource(resource) |
33 | { |
34 | } |
35 | |
36 | bool ResourceListItem::onProcessMessage(ui::Message* msg) |
37 | { |
38 | switch (msg->type()) { |
39 | case kMouseLeaveMessage: |
40 | case kMouseEnterMessage: |
41 | invalidate(); |
42 | break; |
43 | } |
44 | return ListItem::onProcessMessage(msg); |
45 | } |
46 | |
47 | void ResourceListItem::onPaint(PaintEvent& ev) |
48 | { |
49 | auto theme = SkinTheme::get(this); |
50 | Graphics* g = ev.graphics(); |
51 | gfx::Rect bounds = clientBounds(); |
52 | gfx::Color bgcolor, fgcolor; |
53 | |
54 | if (isSelected()) { |
55 | bgcolor = theme->colors.listitemSelectedFace(); |
56 | fgcolor = theme->colors.listitemSelectedText(); |
57 | } |
58 | else { |
59 | bgcolor = theme->colors.listitemNormalFace(); |
60 | fgcolor = theme->colors.listitemNormalText(); |
61 | } |
62 | |
63 | g->fillRect(bgcolor, bounds); |
64 | |
65 | static_cast<ResourcesListBox*>(parent())-> |
66 | paintResource(g, bounds, m_resource.get()); |
67 | |
68 | g->drawText(text(), fgcolor, gfx::ColorNone, |
69 | gfx::Point( |
70 | bounds.x + 2*guiscale(), |
71 | bounds.y + bounds.h/2 - g->measureUIText(text()).h/2)); |
72 | } |
73 | |
74 | void ResourceListItem::onSizeHint(SizeHintEvent& ev) |
75 | { |
76 | ev.setSizeHint( |
77 | static_cast<ResourcesListBox*>(parent())-> |
78 | resourceSizeHint(m_resource.get())); |
79 | } |
80 | |
81 | ////////////////////////////////////////////////////////////////////// |
82 | // ResourcesListBox::LoadingItem |
83 | |
84 | class ResourcesListBox::LoadingItem : public ListItem { |
85 | public: |
86 | LoadingItem() |
87 | : ListItem("Loading" ) |
88 | , m_state(0) { |
89 | } |
90 | |
91 | void makeProgress() { |
92 | std::string text = "Loading " ; |
93 | |
94 | switch ((++m_state) % 4) { |
95 | case 0: text += "/" ; break; |
96 | case 1: text += "-" ; break; |
97 | case 2: text += "\\" ; break; |
98 | case 3: text += "|" ; break; |
99 | } |
100 | |
101 | setText(text); |
102 | } |
103 | |
104 | private: |
105 | int m_state; |
106 | }; |
107 | |
108 | ////////////////////////////////////////////////////////////////////// |
109 | // ResourcesListBox |
110 | |
111 | ResourcesListBox::ResourcesListBox(ResourcesLoader* resourcesLoader) |
112 | : m_resourcesLoader(resourcesLoader) |
113 | , m_resourcesTimer(100) |
114 | , m_reload(false) |
115 | , m_loadingItem(nullptr) |
116 | { |
117 | m_resourcesTimer.Tick.connect([this]{ onTick(); }); |
118 | } |
119 | |
120 | Resource* ResourcesListBox::selectedResource() |
121 | { |
122 | if (ResourceListItem* listItem = dynamic_cast<ResourceListItem*>(getSelectedChild())) |
123 | return listItem->resource(); |
124 | else |
125 | return nullptr; |
126 | } |
127 | |
128 | void ResourcesListBox::reload() |
129 | { |
130 | auto children = this->children(); // Create a copy because we'll |
131 | // modify the list in the for() |
132 | |
133 | // Delete all ResourcesListItem. (PalettesListBox contains a tooltip |
134 | // manager too, so we cannot remove just all children.) |
135 | for (auto child : children) { |
136 | if (dynamic_cast<ResourceListItem*>(child)) |
137 | delete child; |
138 | } |
139 | |
140 | m_reload = true; |
141 | } |
142 | |
143 | void ResourcesListBox::paintResource(Graphics* g, gfx::Rect& bounds, Resource* resource) |
144 | { |
145 | onPaintResource(g, bounds, resource); |
146 | } |
147 | |
148 | gfx::Size ResourcesListBox::resourceSizeHint(Resource* resource) |
149 | { |
150 | gfx::Size pref(0, 0); |
151 | onResourceSizeHint(resource, pref); |
152 | return pref; |
153 | } |
154 | |
155 | bool ResourcesListBox::onProcessMessage(ui::Message* msg) |
156 | { |
157 | switch (msg->type()) { |
158 | |
159 | case kOpenMessage: { |
160 | if (m_reload) { |
161 | m_reload = false; |
162 | m_resourcesLoader->reload(); |
163 | } |
164 | |
165 | m_resourcesTimer.start(); |
166 | break; |
167 | } |
168 | |
169 | } |
170 | return ListBox::onProcessMessage(msg); |
171 | } |
172 | |
173 | void ResourcesListBox::onChange() |
174 | { |
175 | Resource* resource = selectedResource(); |
176 | if (resource) |
177 | onResourceChange(resource); |
178 | } |
179 | |
180 | ResourceListItem* ResourcesListBox::onCreateResourceItem(Resource* resource) |
181 | { |
182 | return new ResourceListItem(resource); |
183 | } |
184 | |
185 | void ResourcesListBox::onTick() |
186 | { |
187 | if (m_resourcesLoader == nullptr) { |
188 | stop(); |
189 | return; |
190 | } |
191 | |
192 | if (!m_loadingItem) { |
193 | m_loadingItem = new LoadingItem; |
194 | addChild(m_loadingItem); |
195 | } |
196 | m_loadingItem->makeProgress(); |
197 | |
198 | std::unique_ptr<Resource> resource; |
199 | std::string name; |
200 | |
201 | while (m_resourcesLoader->next(resource)) { |
202 | std::unique_ptr<ResourceListItem> listItem(onCreateResourceItem(resource.get())); |
203 | insertChild(getItemsCount()-1, listItem.get()); |
204 | sortItems(); |
205 | layout(); |
206 | |
207 | if (View* view = View::getView(this)) |
208 | view->updateView(); |
209 | |
210 | resource.release(); |
211 | listItem.release(); |
212 | } |
213 | |
214 | if (m_resourcesLoader->isDone()) { |
215 | FinishLoading(); |
216 | stop(); |
217 | } |
218 | } |
219 | |
220 | void ResourcesListBox::stop() |
221 | { |
222 | m_resourcesTimer.stop(); |
223 | |
224 | if (m_loadingItem) { |
225 | removeChild(m_loadingItem); |
226 | layout(); |
227 | |
228 | delete m_loadingItem; |
229 | m_loadingItem = nullptr; |
230 | |
231 | if (View* view = View::getView(this)) |
232 | view->updateView(); |
233 | } |
234 | } |
235 | |
236 | } // namespace app |
237 | |