diff --git a/include/llmr/style/style_layer.hpp b/include/llmr/style/style_layer.hpp index fe5d3c3a6..b45982b79 100644 --- a/include/llmr/style/style_layer.hpp +++ b/include/llmr/style/style_layer.hpp @@ -5,11 +5,13 @@ #include #include #include +#include #include #include #include #include +#include namespace llmr { @@ -33,6 +35,15 @@ public: bool isBackground() const; + // Sets the list of classes and creates transitions to the currently applied values. + void setClasses(const std::vector &class_names, timestamp now, + const ClassPropertyTransition &defaultTransition); + +private: + // Applies all properties from a class, if they haven't been applied already. + void applyClassProperties(ClassID class_id, std::set &already_applied, + timestamp now, const ClassPropertyTransition &defaultTransition); + public: // The name of this layer. const std::string id; @@ -44,6 +55,12 @@ public: // Contains all style classes that can be applied to this layer. const std::map styles; +private: + // For every property, stores a list of applied property values, with + // optional transition times. + std::map appliedStyle; + +public: // Stores the evaluated, and cascaded styling information, specific to this // layer's type. StyleProperties style; diff --git a/src/style/style_layer.cpp b/src/style/style_layer.cpp index f60f3f064..eccadbeed 100644 --- a/src/style/style_layer.cpp +++ b/src/style/style_layer.cpp @@ -1,4 +1,5 @@ #include +#include namespace llmr { @@ -10,4 +11,81 @@ bool StyleLayer::isBackground() const { return id == "background"; } +void StyleLayer::setClasses(const std::vector &class_names, const timestamp now, + const ClassPropertyTransition &defaultTransition) { + // Stores all keys that we have already added transitions for. + std::set already_applied; + + // Reverse iterate through all class names and apply them last to first. + for (auto it = class_names.rbegin(); it != class_names.rend(); it++) { + const std::string &class_name = *it; + // From here on, we're only dealing with IDs to avoid comparing strings all the time. + const ClassID class_id = ClassDictionary::Lookup(class_name); + applyClassProperties(class_id, already_applied, now, defaultTransition); + } + + // As the last class, apply the default class. + applyClassProperties(ClassID::Default, already_applied, now, defaultTransition); + + // Make sure that we also transition to the fallback value for keys that aren't changed by + // any applied classes. + for (std::pair &property_pair : appliedStyle) { + const PropertyKey key = property_pair.first; + if (already_applied.find(key) != already_applied.end()) { + // This property has already been set by a previous class, so we don't need to + // transition to the fallback. + continue; + } + + AppliedClassProperties &appliedProperties = property_pair.second; + // Make sure that we don't do double transitions to the fallback value. + if (appliedProperties.mostRecent() != ClassID::Fallback) { + // This property key hasn't been set by a previous class, so we need to add a transition + // to the fallback value for that key. + const timestamp begin = now + defaultTransition.delay; + const timestamp end = begin + defaultTransition.duration; + const PropertyValue &value = PropertyFallbackValue::Get(key); + appliedProperties.add(ClassID::Fallback, begin, end, value); + } + } +} + +// Helper function for applying all properties of a a single class that haven't been applied yet. +void StyleLayer::applyClassProperties(const ClassID class_id, + std::set &already_applied, timestamp now, + const ClassPropertyTransition &defaultTransition) { + auto style_it = styles.find(class_id); + if (style_it == styles.end()) { + // There is no class in this layer with this class_name. + return; + } + + // Loop through all the properties in this style, and add transitions to them, if they're + // not already the most recent transition. + const ClassProperties &properties = style_it->second; + for (const std::pair &property_pair : properties) { + PropertyKey key = property_pair.first; + if (already_applied.find(key) != already_applied.end()) { + // This property has already been set by a previous class. + continue; + } + + // Mark this property as written by a previous class, so that subsequent + // classes won't override this. + already_applied.insert(key); + + // If the most recent transition is not the one with the highest priority, create + // a transition. + AppliedClassProperties &appliedProperties = appliedStyle[key]; + if (appliedProperties.mostRecent() != class_id) { + const ClassPropertyTransition &transition = + properties.getTransition(key, defaultTransition); + const timestamp begin = now + transition.delay; + const timestamp end = begin + transition.duration; + const PropertyValue &value = property_pair.second; + appliedProperties.add(class_id, begin, end, value); + } + } +} + }