Skip to content
Merged

Dev #49

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
69a0208
Add permissions for GitHub Actions workflow
otto-link Aug 20, 2026
71d2e70
Add CODEOWNERS file for otto-link
otto-link Aug 20, 2026
c9c2ba0
feat: add option to synchronize shared attributes across containers i…
otto-link Aug 27, 2026
9aef1d2
Merge pull request #44 from ottolink-dev/40-feat-add-option-to-synchr…
otto-link Aug 27, 2026
6f25613
feat(core): add set_current_to_first method to ContainerGroup
otto-link Aug 27, 2026
34f428e
feat(qt): pluggable widget designs for attribute rows
Leonhardmaster2 Aug 28, 2026
556fe1f
feat(qt): register the stock widgets as a design
Leonhardmaster2 Sep 1, 2026
a1fd947
feat(qt): derive the base theme from QPalette
Leonhardmaster2 Sep 1, 2026
2ba3c87
feat(qt): add the industrial int slider and section chrome
Leonhardmaster2 Sep 1, 2026
d64600e
feat(qt): own the combo popup, animate sections, reserve the scrollbar
Leonhardmaster2 Sep 1, 2026
4fd032a
feat(qt): section cards, popup reveal and a square points canvas
Leonhardmaster2 Sep 1, 2026
8297985
fix(qt): preserve color stop positions when editing or moving a stop …
otto-link Sep 3, 2026
e18c184
feat(qt): unify DesignRegistry as full design bundle with container i…
otto-link Sep 3, 2026
138bafd
refactor(qt): move stock widget implementations to source files and r…
otto-link Sep 3, 2026
7522656
Merge remote-tracking branch 'origin/dev' into feat/industrial-design
Leonhardmaster2 Sep 4, 2026
6739414
fix(qt): let the industrial section factory follow the design's theme
Leonhardmaster2 Sep 4, 2026
da7bf4a
Merge pull request #47 from ottolink-dev/feat/industrial-design
otto-link Sep 4, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
* otto-link
5 changes: 5 additions & 0 deletions .github/workflows/deploy_docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@ on:
push:
branches: [main, dev]

permissions:
contents: write
pages: write
id-token: write

jobs:
doxygen:
runs-on: ubuntu-latest
Expand Down
4 changes: 4 additions & 0 deletions Meta/include/meta/core/abstract_attribute.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

#include <nlohmann/json.hpp>

#include "meta/core/event.hpp"
#include "meta/core/meta_object.hpp"
#include "meta/serialization/serialization_mode.hpp"

Expand All @@ -36,6 +37,9 @@ class AbstractAttribute : public MetaObject
public:
virtual ~AbstractAttribute() = default;

/// Untyped event fired whenever the attribute value changes.
Event<AbstractAttribute &> value_changed_event;

/// Returns the attribute name.
virtual const std::string &name() const = 0;

Expand Down
21 changes: 19 additions & 2 deletions Meta/include/meta/core/attribute.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,22 @@ template <typename T> class Attribute : public AbstractAttribute
Attribute(std::string name, T value)
: name_(std::move(name)), value_(std::move(value))
{
value_changed_conn_ = value_changed.subscribe(
[this](const T &) { value_changed_event.notify(*this); });
}

/// Sets value and notifies subscribers.
void set_value(const T &new_value)
{
value_ = new_value;
value_changed.notify(value_);
}

/// Sets value (by move) and notifies subscribers.
void set_value(T &&new_value)
{
value_ = std::move(new_value);
value_changed.notify(value_);
}

/// Get attribute name.
Expand Down Expand Up @@ -179,8 +195,9 @@ template <typename T> class Attribute : public AbstractAttribute
}

private:
std::string name_;
T value_;
std::string name_;
T value_;
EventConnection value_changed_conn_;
};

} // namespace meta
11 changes: 11 additions & 0 deletions Meta/include/meta/core/attribute_container.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@ concept StringLike = std::is_same_v<std::decay_t<T>, std::string> ||
class AttributeContainer : public MetaObject
{
public:
Event<AbstractAttribute &> attribute_added;
Event<const std::string &> attribute_removed;

// -------------------------------------------------------------------------
// Capacity
// -------------------------------------------------------------------------
Expand Down Expand Up @@ -140,6 +143,8 @@ class AttributeContainer : public MetaObject
// keep track of insertion order
insertion_order_.push_back(name);

attribute_added.notify(*ptr);

return ptr;
}

Expand Down Expand Up @@ -174,6 +179,8 @@ class AttributeContainer : public MetaObject
// keep track of insertion order
insertion_order_.push_back(name);

attribute_added.notify(*ptr);

return ptr;
}

Expand All @@ -197,6 +204,8 @@ class AttributeContainer : public MetaObject
// keep track of insertion order
insertion_order_.push_back(name);

attribute_added.notify(*ptr);

return ptr;
}

Expand Down Expand Up @@ -226,6 +235,8 @@ class AttributeContainer : public MetaObject
// keep track of insertion order
insertion_order_.push_back(name);

attribute_added.notify(*ptr);

return ptr;
}

Expand Down
85 changes: 84 additions & 1 deletion Meta/include/meta/core/container_group.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
#include <memory>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <vector>

#include "meta/core/attribute_container.hpp"
#include "meta/core/meta_object.hpp"
Expand Down Expand Up @@ -81,7 +83,7 @@ class ContainerGroup : public MetaObject
*/
const AttributeContainer *find(const std::string &key) const;

/// Returns the attribute names in insertion order.
/// Returns the container names in insertion order.
const std::vector<std::string> &insertion_order() const;

/**
Expand All @@ -90,9 +92,77 @@ class ContainerGroup : public MetaObject
*/
void set_current(const std::string &key);

/**
* @brief Set the active container to the first container in the list.
* @throws std::runtime_error if there are no containers in the group.
*/
void set_current_to_first();

/// Clear all containers.
void clear();

// -------------------------------------------------------------------------
// Attribute Synchronization
// -------------------------------------------------------------------------

/**
* @brief Detects attribute keys shared across at least two containers with
* matching types.
* @return Vector of shared attribute keys in insertion order.
*/
std::vector<std::string> shared_attributes() const;

/**
* @brief Checks whether an attribute key is shared across at least two
* containers with matching types.
* @param key Attribute identifier.
* @return true if shared, false otherwise.
*/
bool is_shared(const std::string &key) const;

/**
* @brief Enable or disable synchronization for an attribute key across
* containers in this group.
*
* When enabled, the attribute's current value (from the active container, or
* the first container containing it) is propagated to all matching
* containers, and subsequent updates in any container are kept in sync.
*
* @param key Attribute identifier.
* @param synchronize Whether to synchronize.
*/
void set_synchronized(const std::string &key, bool synchronize = true);

/**
* @brief Check whether an attribute is marked for synchronization.
* @param key Attribute identifier.
* @return true if synchronized.
*/
bool is_synchronized(const std::string &key) const;

/**
* @brief Get the set of currently synchronized attribute keys.
* @return Constant reference to the set of synchronized attribute names.
*/
const std::unordered_set<std::string> &synchronized_attributes() const;

/**
* @brief Synchronizes all detected shared attributes across containers.
* @param synchronize Whether to enable or disable synchronization for all.
*/
void synchronize_all(bool synchronize = true);

/// Disables synchronization for all attributes.
void clear_synchronizations();

/**
* @brief Manually propagate the value of an attribute from the active
* container (or first containing container) to all other matching
* containers in the group.
* @param key Attribute identifier.
*/
void sync_attribute(const std::string &key);

// -------------------------------------------------------------------------
// Serialization
// -------------------------------------------------------------------------
Expand Down Expand Up @@ -135,6 +205,19 @@ class ContainerGroup : public MetaObject
std::vector<std::string> insertion_order_;
AttributeContainer *current_ = nullptr;

std::unordered_set<std::string> synchronized_attributes_;
bool is_synchronizing_ = false;

// Track event connections per container
std::unordered_map<std::string, std::vector<EventConnection>>
container_connections_;

void bind_container(const std::string &key, AttributeContainer &container);
void bind_attribute(const std::string &container_key,
AbstractAttribute &attr);
void sync_attribute_across_containers(const std::string &key,
AbstractAttribute &source);

/**
* @brief Removes stale entries from insertion_order_ that no longer exist
* in attributes_ (e.g. after external erasure or clear).
Expand Down
5 changes: 5 additions & 0 deletions Meta/src/attribute_container.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ ConstAttrIterator AttributeContainer::cend() const
void AttributeContainer::clear()
{
Logger::log()->trace("AttributeContainer::clear");

for (const auto &name : insertion_order_)
attribute_removed.notify(name);

attributes_.clear();
compact_insertion_order();
}
Expand Down Expand Up @@ -179,6 +183,7 @@ void AttributeContainer::json_from(const nlohmann::json &j,
std::move(new_attr));

it = insert_it;
attribute_added.notify(*it->second);
}
else
{
Expand Down
Loading
Loading