yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
asset_browser.h
Go to the documentation of this file.
1#ifndef YAZE_APP_GUI_ASSET_BROWSER_H
2#define YAZE_APP_GUI_ASSET_BROWSER_H
3
4#include <array>
5#include <string>
6
8#include "imgui/imgui.h"
9#include "rom/rom.h"
10#include "zelda3/game_data.h"
11
12#define IM_MIN(A, B) (((A) < (B)) ? (A) : (B))
13#define IM_MAX(A, B) (((A) >= (B)) ? (A) : (B))
14#define IM_CLAMP(V, MN, MX) ((V) < (MN) ? (MN) : (V) > (MX) ? (MX) : (V))
15
16namespace yaze {
17namespace gui {
18
19// Extra functions to add deletion support to ImGuiSelectionBasicStorage
20struct ExampleSelectionWithDeletion : ImGuiSelectionBasicStorage {
21 // Find which item should be Focused after deletion.
22 // Call _before_ item submission. Retunr an index in the before-deletion item
23 // list, your item loop should call SetKeyboardFocusHere() on it. The
24 // subsequent ApplyDeletionPostLoop() code will use it to apply Selection.
25 // - We cannot provide this logic in core Dear ImGui because we don't have
26 // access to selection data.
27 // - We don't actually manipulate the ImVector<> here, only in
28 // ApplyDeletionPostLoop(), but using similar API for consistency and
29 // flexibility.
30 // - Important: Deletion only works if the underlying ImGuiID for your items
31 // are stable: aka not depend on their index, but on e.g. item id/ptr.
32 // FIXME-MULTISELECT: Doesn't take account of the possibility focus target
33 // will be moved during deletion. Need refocus or scroll offset.
34 int ApplyDeletionPreLoop(ImGuiMultiSelectIO* ms_io, int items_count) {
35 if (Size == 0)
36 return -1;
37
38 // If focused item is not selected...
39 const int focused_idx =
40 (int)ms_io->NavIdItem; // Index of currently focused item
41 if (ms_io->NavIdSelected == false) // This is merely a shortcut, ==
42 // Contains(adapter->IndexToStorage(items, focused_idx))
43 {
44 ms_io->RangeSrcReset =
45 true; // Request to recover RangeSrc from NavId next frame. Would be
46 // ok to reset even when NavIdSelected==true, but it would take
47 // an extra frame to recover RangeSrc when deleting a selected
48 // item.
49 return focused_idx; // Request to focus same item after deletion.
50 }
51
52 // If focused item is selected: land on first unselected item after focused
53 // item.
54 for (int idx = focused_idx + 1; idx < items_count; idx++)
55 if (!Contains(GetStorageIdFromIndex(idx)))
56 return idx;
57
58 // If focused item is selected: otherwise return last unselected item before
59 // focused item.
60 for (int idx = IM_MIN(focused_idx, items_count) - 1; idx >= 0; idx--)
61 if (!Contains(GetStorageIdFromIndex(idx)))
62 return idx;
63
64 return -1;
65 }
66
67 // Rewrite item list (delete items) + update selection.
68 // - Call after EndMultiSelect()
69 // - We cannot provide this logic in core Dear ImGui because we don't have
70 // access to your items, nor to selection data.
71 template <typename ITEM_TYPE>
72 void ApplyDeletionPostLoop(ImGuiMultiSelectIO* ms_io,
73 ImVector<ITEM_TYPE>& items,
74 int item_curr_idx_to_select) {
75 // Rewrite item list (delete items) + convert old selection index (before
76 // deletion) to new selection index (after selection). If NavId was not part
77 // of selection, we will stay on same item.
78 ImVector<ITEM_TYPE> new_items;
79 new_items.reserve(items.Size - Size);
80 int item_next_idx_to_select = -1;
81 for (int idx = 0; idx < items.Size; idx++) {
82 if (!Contains(GetStorageIdFromIndex(idx)))
83 new_items.push_back(items[idx]);
84 if (item_curr_idx_to_select == idx)
85 item_next_idx_to_select = new_items.Size - 1;
86 }
87 items.swap(new_items);
88
89 // Update selection
90 Clear();
91 if (item_next_idx_to_select != -1 && ms_io->NavIdSelected)
92 SetItemSelected(GetStorageIdFromIndex(item_next_idx_to_select), true);
93 }
94};
95
97 ImGuiID ID;
98 int Type;
99
100 AssetObject(ImGuiID id, int type) {
101 ID = id;
102 Type = type;
103 }
104
105 static const ImGuiTableSortSpecs* s_current_sort_specs;
106
107 static void SortWithSortSpecs(ImGuiTableSortSpecs* sort_specs,
108 AssetObject* items, int items_count) {
109 // Store in variable accessible by the sort function.
110 s_current_sort_specs = sort_specs;
111 if (items_count > 1)
112 qsort(items, (size_t)items_count, sizeof(items[0]),
115 }
116
117 // Compare function to be used by qsort()
118 static int CompareWithSortSpecs(const void* lhs, const void* rhs) {
119 const AssetObject* a = (const AssetObject*)lhs;
120 const AssetObject* b = (const AssetObject*)rhs;
121 for (int n = 0; n < s_current_sort_specs->SpecsCount; n++) {
122 const ImGuiTableColumnSortSpecs* sort_spec =
123 &s_current_sort_specs->Specs[n];
124 int delta = 0;
125 if (sort_spec->ColumnIndex == 0)
126 delta = ((int)a->ID - (int)b->ID);
127 else if (sort_spec->ColumnIndex == 1)
128 delta = (a->Type - b->Type);
129 if (delta > 0)
130 return (sort_spec->SortDirection == ImGuiSortDirection_Ascending) ? +1
131 : -1;
132 if (delta < 0)
133 return (sort_spec->SortDirection == ImGuiSortDirection_Ascending) ? -1
134 : +1;
135 }
136 return ((int)a->ID - (int)b->ID);
137 }
138};
139
140struct UnsortedAsset : public AssetObject {
141 UnsortedAsset(ImGuiID id) : AssetObject(id, 0) {}
142};
143
144struct DungeonAsset : public AssetObject {
145 DungeonAsset(ImGuiID id) : AssetObject(id, 1) {}
146};
147
149 OverworldAsset(ImGuiID id) : AssetObject(id, 2) {}
150};
151
152struct SpriteAsset : public AssetObject {
153 SpriteAsset(ImGuiID id) : AssetObject(id, 3) {}
154};
155
157 // Options
158 bool ShowTypeOverlay = true;
159 bool AllowSorting = true;
161 bool AllowBoxSelect = true;
162 float IconSize = 32.0f;
163 int IconSpacing = 10;
164 // Increase hit-spacing if you want to make it possible to clear or
165 // box-select from gaps. Some spacing is required to able to amend
166 // with Shift+box-select. Value is small in Explorer.
168 bool StretchSpacing = true;
169
170 // State
171 ImVector<AssetObject> Items;
172
173 // (ImGuiSelectionBasicStorage + helper funcs to handle deletion)
175
176 ImGuiID NextItemId = 0; // Unique identifier when creating new items
177 bool RequestDelete = false; // Deferred deletion request
178 bool RequestSort = false; // Deferred sort request
179 // Mouse wheel accumulator to handle smooth wheels better
180 float ZoomWheelAccum = 0.0f;
181
182 // Calculated sizes for layout, output of UpdateLayoutSizes(). Could be locals
183 // but our code is simpler this way.
185 ImVec2 LayoutItemStep; // == LayoutItemSize + LayoutItemSpacing
186 float LayoutItemSpacing = 0.0f;
188 float LayoutOuterPadding = 0.0f;
191 bool Initialized = false;
192
193 void Initialize(const std::array<gfx::Bitmap, zelda3::kNumGfxSheets>& bmp_manager) {
194 // Load the assets
195 for (int i = 0; i < zelda3::kNumGfxSheets; i++) {
196 Items.push_back(UnsortedAsset(i));
197 }
198 Initialized = true;
199 }
200
201 void AddItems(int count) {
202 if (Items.Size == 0)
203 NextItemId = 0;
204 Items.reserve(Items.Size + count);
205 for (int n = 0; n < count; n++, NextItemId++)
206 Items.push_back(AssetObject(NextItemId, (NextItemId % 20) < 15 ? 0
207 : (NextItemId % 20) < 18 ? 1
208 : 2));
209 RequestSort = true;
210 }
211 void ClearItems() {
212 Items.clear();
213 Selection.Clear();
214 }
215
216 // Logic would be written in the main code BeginChild() and outputing to local
217 // variables. We extracted it into a function so we can call it easily from
218 // multiple places.
219 void UpdateLayoutSizes(float avail_width) {
220 // Layout: when not stretching: allow extending into right-most spacing.
222 if (StretchSpacing == false)
223 avail_width += floorf(LayoutItemSpacing * 0.5f);
224
225 // Layout: calculate number of icon per line and number of lines
226 LayoutItemSize = ImVec2(floorf(IconSize * 4), floorf(IconSize));
228 IM_MAX((int)(avail_width / (LayoutItemSize.x + LayoutItemSpacing)), 1);
230
231 // Layout: when stretching: allocate remaining space to more spacing. Round
232 // before division, so item_spacing may be non-integer.
235 floorf(avail_width - LayoutItemSize.x * LayoutColumnCount) /
237
241 IM_MAX(floorf(LayoutItemSpacing) - IconHitSpacing, 0.0f);
242 LayoutOuterPadding = floorf(LayoutItemSpacing * 0.5f);
243 }
244
245 void Draw(const std::array<gfx::Bitmap, zelda3::kNumGfxSheets>& bmp_manager);
246};
247
248} // namespace gui
249
250} // namespace yaze
251
252#endif // YAZE_APP_GUI_ASSET_BROWSER_H
#define IM_MAX(A, B)
#define IM_MIN(A, B)
constexpr uint32_t kNumGfxSheets
Definition game_data.h:25
static const ImGuiTableSortSpecs * s_current_sort_specs
static void SortWithSortSpecs(ImGuiTableSortSpecs *sort_specs, AssetObject *items, int items_count)
AssetObject(ImGuiID id, int type)
static int CompareWithSortSpecs(const void *lhs, const void *rhs)
int ApplyDeletionPreLoop(ImGuiMultiSelectIO *ms_io, int items_count)
void ApplyDeletionPostLoop(ImGuiMultiSelectIO *ms_io, ImVector< ITEM_TYPE > &items, int item_curr_idx_to_select)
void Initialize(const std::array< gfx::Bitmap, zelda3::kNumGfxSheets > &bmp_manager)
void UpdateLayoutSizes(float avail_width)
ExampleSelectionWithDeletion Selection
ImVector< AssetObject > Items
void Draw(const std::array< gfx::Bitmap, zelda3::kNumGfxSheets > &bmp_manager)