// Grim.h #ifndef GRIM_H #define GRIM_H #include #include #include #include #include #include #include #include #include // TODO: // * Document object // * templates class Attribute { private: std::string VALUE; public: Attribute(const std::string& value) : VALUE(value) {} std::optional String() const {return VALUE;} std::optional Int() const { int iVal = 0; auto [ptr, ec] = std::from_chars(VALUE.data(), VALUE.data() + VALUE.size(), iVal); if (ec == std::errc()) { return iVal; } else { return std::nullopt; } } std::optional Double() const { int dVal = 0.0; auto [ptr, ec] = std::from_chars(VALUE.data(), VALUE.data() + VALUE.size(), dVal); if (ec == std::errc()) { return dVal; } else { return std::nullopt; } } std::optional Bool() const { if (VALUE == "true") { return true; } else if (VALUE == "false") { return false; } else { return std::nullopt; } } operator std::string() const { return VALUE; } operator int() const { if (auto val = Int()) { return *val; } throw std::runtime_error("Cannot convert '" + VALUE + "' to int."); } operator double() const { if (auto val = Double()) { return *val; } throw std::runtime_error("Cannot convert '" + VALUE + "' to double."); } operator bool() const { if (auto val = Bool()) { return *val; } throw std::runtime_error("Cannot convert '" + VALUE + "' to bool."); }; }; class Node { private: std::string TagName; std::unordered_map attributes; public: Node* parent; std::vector> children; struct ClassList { private: Node* self; std::vector> indexes; size_t len; // No return, modifies indexes directly void createIndex(); bool checkIndex(); public: ClassList(Node* node) : self(node) {} void add(std::string value); void remove(std::string value); void toggle(std::string value); void replace(std::string key, std::string value); bool contains(std::string value); std::string item(size_t key); size_t length(); }; ClassList classList; struct StyleList { private: Node* self; std::vector> indexes; size_t len; void createIndex(); bool checkIndex(); public: StyleList(Node* node) : self(node) {} std::string get(std::string key); void set(std::string key, std::string value); void remove(std::string key); std::pair item(size_t index); size_t length(); }; StyleList style; Node() : parent(nullptr), classList(this), style(this) { } std::string getTagName() const; void setTagName(const std::string& name); Node* createElement(std::string name); void setAttribute(const std::string& name, const std::string& value); std::optional getAttribute(const std::string& name) const; const std::unordered_map& getAttributes() const; std::vector getAttributeKeys(); std::string print(int indent = 0); }; class Document { public: Node* body; //location; }; std::vector> parseSelectorParts(std::string_view); bool testSelector(Node*, std::vector>); enum class SheetType { CONTAINER, COUNTER, FONTFACE, FONTFEATURE, SWASH, ANNOTATION, ORNAMENTS, STYLISTIC, STYLESET, CHARACTERVARIANT, FONTPALETTEVALUES, IMPORT, KEYFRAMES, LAYER, NAMESPACE, PAGE, PROPERTY, ROOT, SHEET, MEDIA, STARTINGSTYLE, STYLE }; // A sheet is a group of styles, layers can be nested for specifity and the order can be set via // @layer order...; // all style sheets are parsed into ROOT layers // the non-layer styles will be placed inside of "styles" and they take priority over the layers // all at rules are placed inside of "children" // defining the order of the layers only effects the children with the type layer // the rest are defined in the order they are parsed class Sheet { public: // basemap: Maps baseparts to a index of the styles vector pointing to a Style object // because processing a CSS selector isn't trivial we want to make a short list of // the styles that can possibly be a match so we aren't spending a lot of compute // on a selector that applies to the wrong element. To do this we take the right most part // of a selector (that is the part that targets the current element) and we do a few checks // like is the tagname the same, does it have that class, and do the id's match. If so that // is a candidate and we run the full selector on it. That includes checking parents,children, // or what ever else the selector needs to match. std::unordered_map> basemap; std::string name; std::string path; std::vector> selector; std::unordered_map properties; SheetType type; std::unordered_map variables; Sheet* parent; std::vector children; }; class Window; class StyleHandler { private: std::vector parseNodeParts(Node* node); // to compute the @ rules. std::unordered_map variables; Window* window; public: size_t index = 0; StyleHandler(Window* w): window(w) {} // Rules from the master.css file are direct styles of this object // all sheets are parsed as children of the ROOT Sheet root; std::unordered_map getStyles(Node* node); void parseStream(std::istream&); // make a function that will return a list of attributes the will cause style changes if set }; enum class AnyHover { None, Hover }; enum class AnyPointer { None, Fine, Coarse }; enum class Orientation { Landscape, Portrait }; enum class ColorScheme { Light, Dark }; enum class Contrast { NoPreference, More, Less }; enum class ReducedMotion { NoPreference, Reduce }; enum class ReducedTransparency { NoPreference, Reduce }; enum class Update { Fast, Slow }; class Window { private: int width = 0; int height = 0; int resolution = 0; AnyHover anyHover = AnyHover::Hover; AnyPointer anyPointer = AnyPointer::Fine; Orientation orientation = Orientation::Landscape; ColorScheme colorScheme = ColorScheme::Light; Contrast contrast = Contrast::NoPreference; ReducedMotion reducedMotion = ReducedMotion::NoPreference; ReducedTransparency reducedTransparency = ReducedTransparency::NoPreference; Update update = Update::Fast; public: StyleHandler CSS; Window(): CSS(this) {} // +---------------------------------------------------+ // | (hldfk) Window Properties | // +---------------------------------------------------+ void setWidth(int value) { width = value; } void setHeight(int value) { height = value; } void setResolution(int value) { resolution = value; } void setMouse(AnyPointer pointer) { if (pointer == AnyPointer::None) { anyHover = AnyHover::None; } else { anyHover = AnyHover::Hover; } anyPointer = pointer; } void setOrientation(Orientation value) { orientation = value; } void setColorScheme(ColorScheme value) { colorScheme = value; } void setContrast(Contrast value) { contrast = value; } void setReducedMotion(ReducedMotion value) { reducedMotion = value; } void setReducedTransparency(ReducedTransparency value) { reducedTransparency = value; } void setUpdate(Update value) { update = value; } // Getters int getWidth() const { return width; } int getHeight() const { return height; } double getAspectRatio() const { if (height == 0) return 0.0; return static_cast(width) / height; } }; #endif // NODE_H