Category.h header
#include <ew/core/Category.h>
Namespace ew::core
Category class
class ew::core::Category
One node in the user-defined codex taxonomy: a named category with a stable id, an optional parent, and the fields defined directly on it. Fields inherit to descendant categories – that composition lives in Taxonomy, which owns the tree; a Category only knows its own fields and its parent. Categories are value types.
The parent is one of TWO kinds, never both: another category, or an ENTITY. An entity parent is what makes Kingdom(category) -> Vaertis(entity) -> Cities(category) expressible, and the two kinds mean different things, because the relations do. Across a CATEGORY boundary the relation is is-a, so everything carries and the exceptions are named in excludedKeys. Across an ENTITY boundary it is belongs-to, so nothing carries and what applies is named in inheritedKeys. Both lists sit on the RECEIVING category, which is the only party that knows what a property means for it.
Taxonomy cannot resolve an entity parent (that needs the content graph, which owns Taxonomy), so the walk that crosses one lives in Project.
Members
ew::core::taxonomy::Category::Category(ew::core::foundation::CategoryId id, QString name)
Creates a root category with stable id and display name.
ew::core::foundation::CategoryId ew::core::taxonomy::Category::id() const
The stable identity of this category. Immutable.
const QString & ew::core::taxonomy::Category::name() const
The display name.
void ew::core::taxonomy::Category::setName(QString name)
Sets the display name.
ew::core::foundation::CategoryId ew::core::taxonomy::Category::parentId() const
The parent category's id, or a null id if this category has no category parent.
void ew::core::taxonomy::Category::setParentId(ew::core::foundation::CategoryId parentId)
Sets the parent category's id. A non-null id clears the parent entity AND the inherited-key list – a category has ONE parent, and an opt-in list means nothing under a category, where everything carries and the pruning is done by excludedKeys instead.
ew::core::foundation::ContentId ew::core::taxonomy::Category::parentEntityId() const
The parent ENTITY's id, or a null id if this category does not hang from an entity.
What makes Kingdom(category) -> Vaertis(entity) -> Cities(category) -> Gareth's Stand expressible: the cities of one kingdom are a category, but they belong to that kingdom rather than to kingdoms in general.
void ew::core::taxonomy::Category::setParentEntityId(ew::core::foundation::ContentId parentEntityId)
Sets the parent entity's id. A non-null id clears the parent category, for the same reason.
const std::vector< QString > & ew::core::taxonomy::Category::inheritedKeys() const
The keys of the properties this category takes from ABOVE its parent entity.
Empty by default, and meaningful only while the category has a parent entity: a category nested under an entity starts with nothing and takes only what it asks for. Inheritance from a parent CATEGORY does not consult this at all – everything carries there, and what a sub-category does not want it names in excludedKeys.
The choice sits on the RECEIVING category rather than as a mark on each property, because the declaring category cannot know what a future nested category will want, and one mark there lands everywhere or nowhere – two Cities categories under two kingdoms may want different subsets, and both are right. A key naming a property that no longer exists above is ignored rather than an error; an ancestor's schema may change underneath.
void ew::core::taxonomy::Category::setInheritedKeys(std::vector< QString > inheritedKeys)
Sets which properties this category takes through its parent entity.
const std::vector< QString > & ew::core::taxonomy::Category::excludedKeys() const
The keys of the properties this category DROPS from what it would otherwise inherit.
The counterpart to the opt-in list above, and the reason the two exist separately: across a CATEGORY boundary the relation is is-a, so everything carries by default and the exceptions are pruned here; across an ENTITY boundary the relation is belongs-to, so nothing carries by default and what applies is named there.
Items declares an item id every weapon and artifact needs; a "Mentioned items" sub-category, for things that are named but never statted, drops it. Without this the only way out is to pull that sub-category up to a root, which destroys the grouping it was made for.
Excluding hides a property, it does not delete anything: an entity's stored VALUE for that key stays exactly where it is, and putting the key back makes it visible again. It applies to this category and to everything beneath, since they inherit through it.
void ew::core::taxonomy::Category::setExcludedKeys(std::vector< QString > excludedKeys)
Sets which inherited properties this category drops.
int ew::core::taxonomy::Category::sortIndex() const
Where this category sits among its siblings when the writer has arranged them by hand; 0 means they have not.
The codex is ordered by name by default, which is what a project of a hundred entries needs to stay findable. But a cast list wants its protagonists at the top, so a writer can drag siblings into an order of their own – and that order has to survive a reload, so it is stored per node rather than kept in the view.
ZERO IS "NOT PLACED", not "first". An unplaced node sorts after every placed one, by the default rule, so something created later in a hand-arranged list appears at the bottom instead of jumping to the top of an arrangement it was never part of. A container is numbered 1..N all at once when anything in it is dragged, so its order is never half stated.
void ew::core::taxonomy::Category::setSortIndex(int sortIndex)
Sets this category's place among its siblings; 0 returns it to the default order.
bool ew::core::taxonomy::Category::isRoot() const
Returns true if this category hangs from nothing at all – neither a category nor an entity.
const std::vector< FieldDefinition > & ew::core::taxonomy::Category::fields() const
The fields defined directly on this category, not counting inherited ones.
void ew::core::taxonomy::Category::addField(FieldDefinition field)
Adds field, replacing any existing field that has the same key.
void ew::core::taxonomy::Category::insertField(std::size_t index, FieldDefinition field)
Inserts field at position index (clamped to the current field count), without replacing a same-key field. Callers ensure field's key is not already present; used to restore a removed field at its original position.
bool ew::core::taxonomy::Category::moveField(QStringView key, std::size_t newIndex)
Moves the field with key to position newIndex (clamped to the valid range), shifting the intervening fields. Returns true if the field existed and was moved.
bool ew::core::taxonomy::Category::removeField(QStringView key)
Removes the field with key. Returns true if a field was removed.
const FieldDefinition * ew::core::taxonomy::Category::field(QStringView key) const
Returns the field defined on this category with key, or nullptr if none.
Warning: The pointer aliases this category's field list and is invalidated by ANY later field mutation – addField, insertField, moveField, removeField, or the commands that wrap them. Read what is needed (copying any string) before mutating; do not hold it across a change. Every current caller reads immediately, and the one that keeps it in a local (the MCP convert-field tool) finishes reading before it pushes its command – correct, but only by ordering, which is why the constraint is stated here rather than left to be re-derived. Reading an invalidated pointer is undefined behaviour, so this contract cannot be pinned by a test.