home readme diff tree note docs

0commit 8afb721e13a9f37189a21eccccc943ad68d081dc
1Author: lakefox <mason@lakefox.net>
2Date:   Thu Jan 1 11:35:15 2026 -0700
3
4    ClassList/StyleList Update: See Note
5
6diff --git a/include/grim.h b/include/grim.h
7index 9496863..e7ef8c6 100755
8--- a/include/grim.h
9+++ b/include/grim.h
10@@ -121,61 +121,71 @@ struct Unit {
11 
12 class Window;
13 
14+struct ClassList {
15+	private:
16+		std::string* classString;
17+		std::vector<std::pair<int, int>> indexes;
18+		size_t len;
19+		// No return, modifies indexes directly
20+		void createIndex();
21+		bool checkIndex();
22+
23+	public:
24+		ClassList(std::string* cs) : classString(cs) {}
25+		void add(std::string value);
26+		void remove(std::string value);
27+		void toggle(std::string value);
28+		void replace(std::string key, std::string value);
29+		bool contains(std::string value);
30+		std::string item(size_t key);
31+		size_t length();
32+};
33+
34+struct StyleList {
35+	private:
36+		std::string* styleString;
37+		std::vector<std::tuple<int, int, int>> indexes;
38+		size_t len;
39+		void createIndex();
40+		bool checkIndex();
41+	public:
42+		StyleList(std::string* sS) : styleString(sS) {}
43+		std::string get(std::string key);
44+		std::vector<Unit> getUnit(std::string key);
45+		void set(std::string key, std::string value);
46+		void remove(std::string key);
47+		std::pair<std::string, std::string> item(size_t index);
48+		size_t length();
49+};
50+
51 class Node {
52 	private:
53-		std::string TagName;
54+		std::string TagName = "";
55 		std::unordered_map<std::string, std::string> attributes;
56+		float em = 0;
57+		float width = 0;
58+		float height = 0;
59 	public:
60 		Window* window;
61 
62 		Node* parent;
63+		
64 		std::vector<Node> children;
65-		struct ClassList {
66-			private:
67-				Node* self;
68-				std::vector<std::pair<int, int>> indexes;
69-				size_t len;
70-				// No return, modifies indexes directly
71-				void createIndex();
72-				bool checkIndex();
73-
74-			public:
75-				ClassList(Node* node) : self(node) {}
76-				void add(std::string value);
77-				void remove(std::string value);
78-				void toggle(std::string value);
79-				void replace(std::string key, std::string value);
80-				bool contains(std::string value);
81-				std::string item(size_t key);
82-				size_t length();
83-		};
84+		ClassList classList() { return ClassList(&this->attributes["class"]); }
85+		StyleList style() { return StyleList(&this->attributes["style"]); }
86 
87-		ClassList classList;
88-
89-		struct StyleList {
90-			private:
91-				Node* self;
92-				std::vector<std::tuple<int, int, int>> indexes;
93-				size_t len;
94-				void createIndex();
95-				bool checkIndex();
96-			public:
97-				StyleList(Node* node) : self(node) {}
98-				std::string get(std::string key);
99-				std::vector<Unit> getUnit(std::string key);
100-				void set(std::string key, std::string value);
101-				void remove(std::string key);
102-				std::pair<std::string, std::string> item(size_t index);
103-				size_t length();
104-		};
105-
106-		StyleList style;
107-
108-		Node() : parent(nullptr), classList(this), style(this) {}
109+		Node() : parent(nullptr) {}
110 
111 		std::string getTagName() const;
112 		void setTagName(const std::string& name);
113 
114+		float getEM() const; // Returns value from the cache
115+		float getWidth() const;
116+		float getHeight() const;
117+		void cacheEM(const float value); // Sets the cache value (should not be manually used)
118+		void cacheWidth(const float value);
119+		void cacheHeight(const float value);
120+
121 		Node* createElement(std::string name);
122 
123 		void setAttribute(const std::string& name, const std::string& value);
124diff --git a/src/grim.cc b/src/grim.cc
125index 6cde02b..1dcbc67 100755
126--- a/src/grim.cc
127+++ b/src/grim.cc
128@@ -103,7 +103,9 @@ static const std::unordered_map<std::string, std::string> DEFAULT_ATTRIBUTE_VALU
129 };
130 
131 // Tag Name is not a attribute so it needs to be handled differently
132-std::string Node::getTagName() const { return TagName; }
133+std::string Node::getTagName() const { 
134+		return TagName;
135+}
136 void Node::setTagName(const std::string& name) { TagName = name; }
137 
138 void Node::setAttribute(const std::string& name, const std::string& value) {
139@@ -145,15 +147,45 @@ const std::unordered_map<std::string, std::string>& Node::getAttributes() const
140     return attributes;
141 }
142 
143+// +---------------------------------------------------+
144+// | (o78ty) Cache                                     |
145+// +---------------------------------------------------+
146+// For frequently accessed values like em, width, height
147+// it makes sense to not fully calculate the value access
148+// during rendering as it is very intensive. The functions
149+// below are used to store/retrive the last calculated
150+// value. cache*() will not set the real value for the 
151+// Nodei, it only effects the interval variables. 
152+
153+float Node::getEM() const {
154+	return em;
155+}
156+float Node::getWidth() const {
157+	return width;
158+}
159+float Node::getHeight() const {
160+	return height;
161+}
162+void Node::cacheEM(const float value) {
163+	em = value;
164+}
165+void Node::cacheWidth(const float value) {
166+	width = value;
167+}
168+void Node::cacheHeight(const float value) {
169+	height = value;
170+}
171+
172+
173 // +---------------------------------------------------+
174 // | (f98y0) ClassList                                 |
175 // +---------------------------------------------------+
176 
177-void Node::ClassList::createIndex() {
178+void ClassList::createIndex() {
179 	int index = 0;
180 	// Add a space to the back to help trigger the adding of the 
181 	// last item
182-	std::string classes = self->attributes["class"]+" ";
183+	std::string classes = *classString+" ";
184 	// Update the stored length
185 	len = classes.length();
186 	indexes.clear();
187@@ -174,9 +206,8 @@ void Node::ClassList::createIndex() {
188 }
189 
190 
191-bool Node::ClassList::checkIndex() {
192-	std::string classes = self->attributes["class"];
193-	if (classes.length() != len) {
194+bool ClassList::checkIndex() {
195+	if (classString->length() != len) {
196 		return false;
197 	} else {
198 		// Skip the first one because there is a space
199@@ -186,7 +217,7 @@ bool Node::ClassList::checkIndex() {
200 		for (size_t i = 1; i < indexes.size(); i++) {
201 			// See if the index position is a space
202 			// if not return false
203-			if (classes[indexes[i-1].first] != ' ') {
204+			if (!std::isspace(classString->at(indexes[i-1].first))) {
205 				return false;
206 			}
207 		}
208@@ -194,7 +225,7 @@ bool Node::ClassList::checkIndex() {
209 	}
210 }
211 
212-size_t Node::ClassList::length() {
213+size_t ClassList::length() {
214 	if (!checkIndex()) {
215 		createIndex();
216 	}
217@@ -202,27 +233,27 @@ size_t Node::ClassList::length() {
218 }
219 
220 
221-std::string Node::ClassList::item(size_t key) {
222+std::string ClassList::item(size_t key) {
223 	if (!checkIndex()) {
224 		createIndex();
225 	}
226 	if (key < indexes.size()) {
227 		std::pair<int, int> pair = indexes[key];
228-		return self->attributes["class"].substr(pair.first, pair.second);
229+		return classString->substr(pair.first, pair.second);
230 	} else {
231 		return "";
232 	}
233 }
234 
235-void Node::ClassList::add(std::string value) {
236+void ClassList::add(std::string value) {
237 	if (!checkIndex()) {
238 		createIndex();
239 	}
240 
241-	if (self->attributes["class"].length() == 0) {
242-		self->attributes["class"] += value;
243+	if (classString->length() == 0) {
244+		classString->append(value);
245 	} else {
246-		self->attributes["class"] += " "+value;
247+		classString->append(" "+value);
248 	}
249 	// len because the index of the added space will be the length of 
250 	// the prevous string
251@@ -231,42 +262,40 @@ void Node::ClassList::add(std::string value) {
252 	len += vl+1;
253 }
254 
255-void Node::ClassList::remove(std::string value) {
256+void ClassList::remove(std::string value) {
257 	if (!checkIndex()) {
258 		createIndex();
259 	}
260 
261 	int newLen = value.length();
262 	size_t cLen = indexes.size();
263-	std::string classes = self->attributes["class"];	
264 
265 	for (size_t i = 0; i < cLen; i++) {
266 		std::pair<int, int> pair = indexes[i];
267 		if (newLen == pair.second) {
268-			if (classes.substr(pair.first, pair.second) == value) {
269+			if (classString->substr(pair.first, pair.second) == value) {
270 				// Splice out the value to be removed 
271 				// ISSUE: This will keep the surounding spaces and cause the size to grow when 
272 				// classes are added and removed
273-				self->attributes["class"] = classes.substr(0, pair.first)+classes.substr(pair.first+pair.second);
274+				classString->erase(pair.first, pair.second);
275 			}
276 		}
277 	}	
278 }
279 
280-bool Node::ClassList::contains(std::string value) {
281+bool ClassList::contains(std::string value) {
282 	if (!checkIndex()) {
283 		createIndex();
284 	}
285 
286 	int newLen = value.length();
287 	size_t cLen = indexes.size();
288-	std::string classes = self->attributes["class"];	
289 	bool found = false;
290 
291 	for (size_t i = 0; i < cLen; i++) {
292 		std::pair<int, int> pair = indexes[i];
293 		if (newLen == pair.second) {
294-			if (classes.substr(pair.first, pair.second) == value) {
295+			if (classString->substr(pair.first, pair.second) == value) {
296 				// Same logic as remove but turns found true then breaks
297 				found = true;
298 				break;
299@@ -276,7 +305,7 @@ bool Node::ClassList::contains(std::string value) {
300 	return found;
301 }
302 
303-void Node::ClassList::toggle(std::string value) {
304+void ClassList::toggle(std::string value) {
305 	if (contains(value)) {
306 		remove(value);
307 	} else {
308@@ -302,10 +331,9 @@ void Node::ClassList::toggle(std::string value) {
309 // getUnit is a interal method to get styles from the stylelist
310 // as parsed units for rendering
311 
312-void Node::StyleList::createIndex() {
313-	std::string styles = self->attributes["style"];
314+void StyleList::createIndex() {
315 	// Update the stored length
316-	len = styles.length();
317+	len = styleString->length();
318 	indexes.clear();
319 
320 	// "  background-color: white;"
321@@ -327,7 +355,7 @@ void Node::StyleList::createIndex() {
322 
323 
324 	for (size_t i = 0; i < len; i++) {
325-		char s = styles[i];
326+		char s = styleString->at(i);
327 		if (firstNonWhiteSpace) {
328 			if (keyFound) {
329 				if (secondNonWhiteSpace) {
330@@ -357,15 +385,14 @@ void Node::StyleList::createIndex() {
331 }
332 
333 
334-bool Node::StyleList::checkIndex() {
335-	std::string styles = self->attributes["style"];
336-	if (styles.length() != len) {
337+bool StyleList::checkIndex() {
338+	if (styleString->length() != len) {
339 		return false;
340 	} else {
341 		for (size_t i = 0; i < indexes.size(); i++) {
342-			if (std::isspace(styles[std::get<0>(indexes[i])]) ||
343-					styles[std::get<1>(indexes[i])] != ':' || 
344-					styles[std::get<2>(indexes[i])] != ';'
345+			if (std::isspace(styleString->at(std::get<0>(indexes[i]))) ||
346+					styleString->at(std::get<1>(indexes[i])) != ':' || 
347+					styleString->at(std::get<2>(indexes[i])) != ';'
348 					) {
349 				return false;
350 			}
351@@ -374,46 +401,44 @@ bool Node::StyleList::checkIndex() {
352 	}
353 }
354 
355-std::pair<std::string, std::string> Node::StyleList::item(size_t index) {
356+std::pair<std::string, std::string> StyleList::item(size_t index) {
357 	if (!checkIndex()) {
358 		createIndex();
359 	}
360 
361 	std::pair<std::string, std::string> property;
362-	std::string styles = self->attributes["style"];
363 
364 	int f = std::get<0>(indexes[index]);
365 	int s = std::get<1>(indexes[index]);
366-	property.first = styles.substr(f, s-f);
367+	property.first = styleString->substr(f, s-f);
368 	trimSpace(property.first);
369 
370 	int t = std::get<2>(indexes[index]);
371-	property.second = styles.substr(s+1, t-(s+1));
372+	property.second = styleString->substr(s+1, t-(s+1));
373 	trimSpace(property.second);
374 	return property;
375 
376 }
377 
378-size_t Node::StyleList::length() {
379+size_t StyleList::length() {
380 	if (!checkIndex()) {
381 		createIndex();
382 	}
383 	return indexes.size();
384 }
385 
386-std::string Node::StyleList::get(std::string key) {
387+std::string StyleList::get(std::string key) {
388 	if (!checkIndex()) {
389 		createIndex();
390 	}
391-	std::string styles = self->attributes["style"];
392 	for (size_t i = 0; i < indexes.size(); i++) {
393 		int f = std::get<0>(indexes[i]);
394 		int s = std::get<1>(indexes[i]);
395-		std::string slicedKey = styles.substr(f, s-f);
396+		std::string slicedKey = styleString->substr(f, s-f);
397 		trimSpace(slicedKey);
398 		if (slicedKey == key) {
399 			int t = std::get<2>(indexes[i]);
400-			std::string value = styles.substr(s+1, t-(s+1));
401+			std::string value = styleString->substr(s+1, t-(s+1));
402 			trimSpace(value);
403 			return value;
404 		}
405@@ -421,19 +446,18 @@ std::string Node::StyleList::get(std::string key) {
406 	return "";
407 }
408 
409-std::vector<Unit> Node::StyleList::getUnit(std::string key) {
410+std::vector<Unit> StyleList::getUnit(std::string key) {
411 	if (!checkIndex()) {
412 		createIndex();
413 	}
414-	std::string styles = self->attributes["style"];
415 	for (size_t i = 0; i < indexes.size(); i++) {
416 		int f = std::get<0>(indexes[i]);
417 		int s = std::get<1>(indexes[i]);
418-		std::string slicedKey = styles.substr(f, s-f);
419+		std::string slicedKey = styleString->substr(f, s-f);
420 		trimSpace(slicedKey);
421 		if (slicedKey == key) {
422 			int t = std::get<2>(indexes[i]);
423-			std::string value = styles.substr(s+1, t-(s+1));
424+			std::string value = styleString->substr(s+1, t-(s+1));
425 			trimSpace(value);
426 			return getUnit(value);
427 		}
428@@ -442,52 +466,53 @@ std::vector<Unit> Node::StyleList::getUnit(std::string key) {
429 	return empty;
430 }
431 
432-void Node::StyleList::set(std::string key, std::string value) {
433+void StyleList::set(std::string key, std::string value) {
434 	if (!checkIndex()) {
435 		createIndex();
436 	}
437-	std::string styles = self->attributes["style"];
438 
439 	for (size_t i = 0; i < indexes.size(); i++) {
440 		int f = std::get<0>(indexes[i]);
441 		int s = std::get<1>(indexes[i]);
442-		std::string slicedKey = styles.substr(f, s-f);
443+
444+		std::string slicedKey = styleString->substr(f, s-f);
445 		trimSpace(slicedKey);
446 		if (slicedKey == key) {
447 			int t = std::get<2>(indexes[i]);
448-			self->attributes["style"] = styles.substr(0, f) +" "+key+":"+value+";"+styles.substr(t);
449+			styleString->replace(f,t-f," "+key+":"+value+";");
450 			// Return early to skip the appending value;
451 			return;
452 		}
453 	}
454 
455 	// If the property wasn't replaced then add it
456-	
457-	char lastChar = styles[std::get<2>(indexes[indexes.size()-1])];
458 
459-	if (lastChar == ';') {
460-		self->attributes["style"] += " "+key+":"+value+";";
461-	} else {
462-		// If the user didn't terminate the last style
463-		// terminate it for them
464-		self->attributes["style"] += "; "+key+":"+value+";";
465+	if (!indexes.empty()) {
466+		char lastChar = styleString->at(std::get<2>(indexes[indexes.size()-1]));
467+
468+		if (lastChar == ';') {
469+			styleString->append(" "+key+":"+value+";");
470+		} else {
471+			// If the user didn't terminate the last style
472+			// terminate it for them
473+			styleString->append("; "+key+":"+value+";");
474+		}
475 	}
476 }
477 
478-void Node::StyleList::remove(std::string key) {
479+void StyleList::remove(std::string key) {
480 	if (!checkIndex()) {
481 		createIndex();
482 	}
483-	std::string styles = self->attributes["style"];
484 
485 	for (size_t i = 0; i < indexes.size(); i++) {
486 		int f = std::get<0>(indexes[i]);
487 		int s = std::get<1>(indexes[i]);
488-		std::string slicedKey = styles.substr(f, s-f);
489+		std::string slicedKey = styleString->substr(f, s-f);
490 		trimSpace(slicedKey);
491 		if (slicedKey == key) {
492 			int t = std::get<2>(indexes[i]);
493-			self->attributes["style"] = styles.substr(0, f) + styles.substr(t);
494+			styleString->erase(f, t);
495 			return;
496 		}
497 	}
498@@ -713,10 +738,12 @@ std::vector<std::string> parseNodeParts(Node* node) {
499 		}
500 	}
501 
502-	auto classLen = node->classList.length();
503+	ClassList cl = node->classList();
504+
505+	size_t classLen = cl.length();
506 
507 	for (size_t i = 0; i < classLen; i++) {
508-		parts.push_back("."+node->classList.item(i));
509+		parts.push_back("."+cl.item(i));
510 	}
511 
512 	parts.push_back("*");
513@@ -1208,12 +1235,7 @@ bool testSelector(Node* node, std::vector<std::vector<std::string>> selector) {
514 	// return at the earliest possible part avoiding unnessisary 
515 	// computing
516 
517-	std::vector<std::string> nodeClasses;
518-	size_t classLen = node->classList.length();
519-	for (size_t i = 0; i < classLen; i++) {
520-		nodeClasses.push_back(node->classList.item(i));
521-	}
522-	
523+	ClassList cl = node->classList();
524 
525 	std::unordered_map<std::string, std::string> attributes = node->getAttributes();
526 	std::vector<std::string> attrs;
527@@ -1318,12 +1340,8 @@ bool testSelector(Node* node, std::vector<std::vector<std::string>> selector) {
528 		} else if (p[0] == '.') {
529 			bool found = false;
530 			p.erase(0,1);
531-			for (auto c : nodeClasses) {
532-				if (p == c) {
533-					found = true;
534-					break;
535-				}
536-			}
537+			found = cl.contains(p);
538+			
539 			matched = found;
540 		} else if (p[0] == '[') {
541 			// This covers all attributes and non relative pseudo-classes
542@@ -1499,9 +1517,6 @@ bool testSelector(Node* node, std::vector<std::vector<std::string>> selector) {
543 
544 				std::string pattern = parts[i+1].substr(1,parts[i+1].length()-2);
545 
546-				std::cout << "################################" << std::endl;
547-				std::cout << pattern << std::endl;
548-
549 				for (size_t i = 0; i < pattern.length(); i++) {
550 					if (!std::isspace(pattern[i])) {
551 						if (pattern[i] == '-' && !negFound) {
552@@ -1590,7 +1605,6 @@ bool testSelector(Node* node, std::vector<std::vector<std::string>> selector) {
553 						index = childIndex;
554 					}
555 				} else {
556-					std::cout << "Has of: " << ofSelector << std::endl;
557 					// Parse the of selector
558 					auto pOS = parseSelectorParts(ofSelector.substr(2));
559 					if (testSelector(node, pOS)) {
560@@ -1647,11 +1661,6 @@ bool testSelector(Node* node, std::vector<std::vector<std::string>> selector) {
561 					A = 1;
562 				}
563 
564-				std::cout << "A: " << A << std::endl;
565-				std::cout << "B: " << B << std::endl;
566-				std::cout << "Sym: " << symbol << std::endl;
567-				std::cout << "OF: " << ofSelector << std::endl;
568-				std::cout << "Index: " << index << std::endl;
569 
570 				if (isBool) {
571 					if (isEven) {