VisuTwin Canvas
C++ 3D Engine — Metal Backend
Loading...
Searching...
No Matches
tags.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: Apache-2.0
2// Copyright 2025-2026 Arnis Lektauers
3//
4// Created by Arnis Lektauers on 05.10.2025.
5//
6#include "tags.h"
7#include <algorithm>
8
9namespace visutwin::canvas
10{
11 void Tags::appendFlat(std::vector<std::string>& out, const std::string& tag)
12 {
13 if (!tag.empty()) {
14 out.push_back(tag);
15 }
16 }
17
18 void Tags::appendFlat(std::vector<std::string>& out, const char* tag)
19 {
20 if (tag && *tag) {
21 out.emplace_back(tag);
22 }
23 }
24
25 void Tags::appendFlat(std::vector<std::string>& out, const std::vector<std::string>& tags)
26 {
27 for (const auto& tag : tags) {
28 appendFlat(out, tag);
29 }
30 }
31
32 void Tags::appendFlat(std::vector<std::string>& out, const std::initializer_list<std::string> tags)
33 {
34 for (const auto& tag : tags) {
35 appendFlat(out, tag);
36 }
37 }
38
39 void Tags::appendQuery(std::vector<std::vector<std::string>>& out, const std::string& tag)
40 {
41 if (!tag.empty()) {
42 out.push_back({tag});
43 }
44 }
45
46 void Tags::appendQuery(std::vector<std::vector<std::string>>& out, const char* tag)
47 {
48 if (tag && *tag) {
49 out.push_back({std::string(tag)});
50 }
51 }
52
53 void Tags::appendQuery(std::vector<std::vector<std::string>>& out, const std::vector<std::string>& tags)
54 {
55 std::vector<std::string> group;
56 group.reserve(tags.size());
57 for (const auto& tag : tags) {
58 if (!tag.empty()) {
59 group.push_back(tag);
60 }
61 }
62 if (!group.empty()) {
63 out.push_back(std::move(group));
64 }
65 }
66
67 void Tags::appendQuery(std::vector<std::vector<std::string>>& out, const std::initializer_list<std::string> tags)
68 {
69 std::vector<std::string> group;
70 group.reserve(tags.size());
71 for (const auto& tag : tags) {
72 if (!tag.empty()) {
73 group.push_back(tag);
74 }
75 }
76 if (!group.empty()) {
77 out.push_back(std::move(group));
78 }
79 }
80
81 bool Tags::add(const std::string& tag)
82 {
83 if (tag.empty()) {
84 return false;
85 }
86
87 if (_index.contains(tag)) {
88 return false;
89 }
90
91 _index.insert(tag);
92 _list.push_back(tag);
93
94 fire(EVENT_ADD, tag, _parent);
95 fire(EVENT_CHANGE, _parent);
96 return true;
97 }
98
99 bool Tags::add(const std::vector<std::string>& tags)
100 {
101 bool changed = false;
102 for (const auto& tag : tags) {
103 if (tag.empty()) {
104 continue;
105 }
106
107 if (_index.contains(tag)) {
108 continue;
109 }
110
111 changed = true;
112 _index.insert(tag);
113 _list.push_back(tag);
114 fire(EVENT_ADD, tag, _parent);
115 }
116
117 if (changed) {
118 fire(EVENT_CHANGE, _parent);
119 }
120
121 return changed;
122 }
123
124 bool Tags::add(std::initializer_list<std::string> tags)
125 {
126 return add(std::vector<std::string>(tags));
127 }
128
129 bool Tags::add(const char* tag)
130 {
131 return add(std::string(tag ? tag : ""));
132 }
133
134 bool Tags::remove(const std::string& tag)
135 {
136 if (tag.empty()) {
137 return false;
138 }
139
140 if (!_index.contains(tag)) {
141 return false;
142 }
143
144 _index.erase(tag);
145 _list.erase(std::remove(_list.begin(), _list.end(), tag), _list.end());
146
147 fire(EVENT_REMOVE, tag, _parent);
148 fire(EVENT_CHANGE, _parent);
149 return true;
150 }
151
152 bool Tags::remove(const std::vector<std::string>& tags)
153 {
154 bool changed = false;
155 for (const auto& tag : tags) {
156 if (tag.empty()) {
157 continue;
158 }
159
160 if (!_index.contains(tag)) {
161 continue;
162 }
163
164 changed = true;
165 _index.erase(tag);
166 _list.erase(std::remove(_list.begin(), _list.end(), tag), _list.end());
167 fire(EVENT_REMOVE, tag, _parent);
168 }
169
170 if (changed) {
171 fire(EVENT_CHANGE, _parent);
172 }
173
174 return changed;
175 }
176
177 bool Tags::remove(std::initializer_list<std::string> tags)
178 {
179 return remove(std::vector<std::string>(tags));
180 }
181
182 bool Tags::remove(const char* tag)
183 {
184 return remove(std::string(tag ? tag : ""));
185 }
186
188 {
189 if (_list.empty()) {
190 return;
191 }
192
193 auto tagsCopy = _list;
194 _list.clear();
195 _index.clear();
196
197 for (const std::string& tag : tagsCopy) {
198 fire(EVENT_REMOVE, tag, _parent);
199 }
200 fire(EVENT_CHANGE, _parent);
201 }
202
203 bool Tags::has(const std::string& tag) const
204 {
205 return !tag.empty() && _index.contains(tag);
206 }
207
208 bool Tags::has(const std::vector<std::string>& tags) const
209 {
210 return hasInternal({tags});
211 }
212
213 bool Tags::has(std::initializer_list<std::string> tags) const
214 {
215 return has(std::vector<std::string>(tags));
216 }
217
218 bool Tags::has(const char* tag) const
219 {
220 return has(std::string(tag ? tag : ""));
221 }
222
223 bool Tags::has(const std::vector<std::vector<std::string>>& query) const
224 {
225 return hasInternal(query);
226 }
227
228 bool Tags::hasInternal(const std::vector<std::vector<std::string>>& query) const
229 {
230 if (_list.empty() || query.empty()) {
231 return false;
232 }
233
234 for (const auto& group : query) {
235 if (group.size() == 1) {
236 if (_index.contains(group[0])) {
237 return true;
238 }
239 continue;
240 }
241
242 bool allPresent = true;
243 for (const std::string& tag : group) {
244 if (!_index.contains(tag)) {
245 allPresent = false;
246 break;
247 }
248 }
249
250 if (allPresent) {
251 return true;
252 }
253 }
254
255 return false;
256 }
257}
EventHandler * fire(const std::string &name, Args &&... args)
static constexpr const char * EVENT_ADD
Definition tags.h:29
bool remove(const std::string &tag)
Definition tags.cpp:134
bool has(const std::string &tag) const
Definition tags.cpp:203
static constexpr const char * EVENT_CHANGE
Definition tags.h:31
static constexpr const char * EVENT_REMOVE
Definition tags.h:30
bool add(const std::string &tag)
Definition tags.cpp:81