Skip to content

Commit

Permalink
Merge pull request #137 from ImJimmi/misc-utils
Browse files Browse the repository at this point in the history
Various useful bits and bobs
  • Loading branch information
ImJimmi authored Feb 7, 2024
2 parents bd953c4 + da003a8 commit 9797773
Show file tree
Hide file tree
Showing 16 changed files with 767 additions and 80 deletions.
13 changes: 13 additions & 0 deletions jive_core/algorithms/jive_Visitor.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#pragma once

namespace jive
{
template <class... Variants>
struct Visitor : Variants...
{
using Variants::operator()...;
};

template <class... Variants>
Visitor(Variants...) -> Visitor<Variants...>;
} // namespace jive
12 changes: 6 additions & 6 deletions jive_core/geometry/jive_Length.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ namespace jive

[[nodiscard]] double Length::getRelativeParentLength(const juce::Rectangle<double>& parentBounds) const
{
jassert(tree.getParent().isValid());
jassert(isValid(getParent(source)));

if (id.toString().containsIgnoreCase("width") || id.toString().containsIgnoreCase("x"))
return parentBounds.getWidth();
Expand All @@ -52,11 +52,11 @@ namespace jive

[[nodiscard]] float Length::getFontSize() const
{
for (auto toSearch = tree;
toSearch.isValid();
toSearch = toSearch.getParent())
for (auto toSearch = source;
isValid(source);
toSearch = getParent(source))
{
if (const auto style = toSearch["style"];
if (const auto style = getVar(toSearch, "style");
style.isObject())
{
if (const auto fontSize = style["font-size"];
Expand All @@ -72,7 +72,7 @@ namespace jive

[[nodiscard]] float Length::getRootFontSize() const
{
if (const auto style = tree.getRoot()["style"];
if (const auto style = getVar(getRoot(source), "style");
style.isObject())
{
if (const auto fontSize = style["font-size"];
Expand Down
2 changes: 2 additions & 0 deletions jive_core/jive_core.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,5 @@
#include "graphics/jive_LookAndFeel.cpp"

#include "interface/jive_ComponentInteractionState.cpp"

#include "time/jive_Timer.cpp"
3 changes: 3 additions & 0 deletions jive_core/jive_core.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ END_JUCE_MODULE_DECLARATION */
#include "logging/jive_StringStreams.h"

#include "algorithms/jive_Find.h"
#include "algorithms/jive_Visitor.h"

#include "values/jive_Colours.h"
#include "values/jive_Event.h"
Expand All @@ -42,3 +43,5 @@ END_JUCE_MODULE_DECLARATION */
#include "graphics/jive_Fill.h"

#include "interface/jive_ComponentInteractionState.h"

#include "time/jive_Timer.h"
5 changes: 5 additions & 0 deletions jive_core/logging/jive_StringStreams.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,9 @@ namespace juce
{
return str << juce::String{ static_cast<double>(value) };
}

String& operator<<(String& str, RelativeTime relativeTime)
{
return str << juce::String{ relativeTime.inSeconds() } << "s";
}
} // namespace juce
14 changes: 14 additions & 0 deletions jive_core/logging/jive_StringStreams.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ namespace juce
String& operator<<(String& str, const var& value);
String& operator<<(String& str, const Time& time);
String& operator<<(String& str, long double value);
String& operator<<(String& str, RelativeTime relativeTime);

template <typename T>
String& operator<<(String& str, const Point<T>& point)
Expand All @@ -29,4 +30,17 @@ namespace juce
<< ", " << rect.getHeight()
<< " }";
}

template <typename T>
String& operator<<(String& str, const std::optional<T>& optional)
{
str << "std::optional<" << typeid(T).name() << "> { ";

if (optional.has_value())
str << *optional;
else
str << "NULL";

return str << " }";
}
} // namespace juce
121 changes: 121 additions & 0 deletions jive_core/time/jive_Timer.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
#include "jive_Timer.h"

namespace jive
{
#if JIVE_UNIT_TESTS
FakeTime::~FakeTime()
{
clearSingletonInstance();
}

void FakeTime::addListener(Listener& listener)
{
listeners.add(&listener);
}

void FakeTime::removeListener(Listener& listener)
{
listeners.remove(&listener);
}

void FakeTime::incrementTime(juce::RelativeTime increment)
{
getInstance()->incrementTimeInternal(increment);
}

juce::Time FakeTime::now() noexcept
{
return getInstance()->currentTime;
}

void FakeTime::incrementTimeInternal(juce::RelativeTime increment)
{
currentTime += increment;
listeners.call(&Listener::timeChanged);
}

JUCE_IMPLEMENT_SINGLETON(FakeTime)
#endif

Timer::Timer(Timer::Callback timerCallback,
juce::RelativeTime callbackInterval)
: callback{ timerCallback }
, interval{ callbackInterval }
{
#if JIVE_UNIT_TESTS
timeLastCallbackInvoked = FakeTime::now();
FakeTime::getInstance()->addListener(*this);
#else
timeLastCallbackInvoked = juce::Time::getCurrentTime();
startTimer(static_cast<int>(interval.inMilliseconds()));
#endif
}

Timer::~Timer()
{
#if JIVE_UNIT_TESTS
FakeTime::getInstance()->removeListener(*this);
#endif
}

#if JIVE_UNIT_TESTS
void Timer::timeChanged()
{
for (auto elapsed = FakeTime::now() - timeLastCallbackInvoked;
elapsed.inMilliseconds() > interval.inMilliseconds();
elapsed -= interval)
{
timeLastCallbackInvoked = FakeTime::now() - elapsed + interval;
callback(timeLastCallbackInvoked);
}
}
#else
void Timer::timerCallback()
{
callback(juce::Time::getCurrentTime());
}
#endif
} // namespace jive

#if JIVE_UNIT_TESTS
class TimerUnitTest : public juce::UnitTest
{
public:
TimerUnitTest()
: juce::UnitTest{ "jive::Timer", "jive" }
{
}

void runTest() final
{
const auto initialTime = jive::FakeTime::now();

beginTest("faking the passing of time");
jive::FakeTime::incrementTime(juce::RelativeTime::seconds(5.3));
expectEquals(jive::FakeTime::now(), initialTime + juce::RelativeTime::seconds(5.3));

beginTest("getting callbacks");
juce::Array<juce::Time> invokeTimes;
jive::Timer timer{
[&invokeTimes](juce::Time now) mutable {
invokeTimes.add(now);
},
juce::RelativeTime::seconds(0.1),
};
jive::FakeTime::incrementTime(juce::RelativeTime::seconds(0.25));
expectEquals(invokeTimes.size(), 2);
expectEquals(invokeTimes[0], initialTime + juce::RelativeTime::seconds(5.4));
expectEquals(invokeTimes[1], initialTime + juce::RelativeTime::seconds(5.5));

jive::FakeTime::incrementTime(juce::RelativeTime::seconds(0.3333));
expectEquals(invokeTimes.size(), 5);
expectEquals(invokeTimes[0], initialTime + juce::RelativeTime::seconds(5.4));
expectEquals(invokeTimes[1], initialTime + juce::RelativeTime::seconds(5.5));
expectEquals(invokeTimes[2], initialTime + juce::RelativeTime::seconds(5.6));
expectEquals(invokeTimes[3], initialTime + juce::RelativeTime::seconds(5.7));
expectEquals(invokeTimes[4], initialTime + juce::RelativeTime::seconds(5.8));
}
};

static TimerUnitTest timerUnitTest;
#endif
68 changes: 68 additions & 0 deletions jive_core/time/jive_Timer.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
#pragma once

namespace jive
{
#if JIVE_UNIT_TESTS
class FakeTime : public juce::DeletedAtShutdown
{
public:
struct Listener
{
virtual ~Listener() = default;

virtual void timeChanged() = 0;
};

FakeTime() = default;
~FakeTime();

void addListener(Listener& listener);
void removeListener(Listener& listener);

static void incrementTime(juce::RelativeTime);
static juce::Time now() noexcept;

JUCE_DECLARE_SINGLETON(FakeTime, false)

private:
void incrementTimeInternal(juce::RelativeTime);

juce::Time currentTime;
juce::ListenerList<Listener> listeners;
};
#endif

class Timer
#if JIVE_UNIT_TESTS
: private FakeTime::Listener
#else
: private juce::Timer
#endif
{
public:
using Callback = std::function<void(juce::Time)>;

Timer(Callback timerCallback,
juce::RelativeTime callbackInterval);
~Timer();

private:
#if JIVE_UNIT_TESTS
void timeChanged() final;
#else
void timerCallback() final;
#endif
Callback callback;
juce::RelativeTime interval;
juce::Time timeLastCallbackInvoked;
};

[[nodiscard]] static inline juce::Time now() noexcept
{
#if JIVE_UNIT_TESTS
return FakeTime::now();
#else
return juce::Time::getCurrentTime();
#endif
}
} // namespace jive
29 changes: 29 additions & 0 deletions jive_core/values/jive_Object.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,9 @@ namespace jive
void Object::setProperty(const juce::Identifier& propertyName,
const juce::var& newValue)
{
if (auto* childObject = dynamic_cast<Object*>(newValue.getDynamicObject()))
childObject->parent = this;

const auto propertyChanged = DynamicObject::getProperties()
.set(propertyName, newValue);

Expand All @@ -94,6 +97,32 @@ namespace jive
return dynamic_cast<juce::DynamicObject*>(const_cast<Object*>(this))->getProperties();
}

Object* Object::getParent() noexcept
{
return parent;
}

const Object* Object::getParent() const noexcept
{
return parent;
}

Object* Object::getRoot() noexcept
{
if (parent == nullptr)
return this;

return parent->getRoot();
}

const Object* Object::getRoot() const noexcept
{
if (parent == nullptr)
return this;

return parent->getRoot();
}

void Object::addListener(Listener& listener) const
{
listeners.add(&listener);
Expand Down
6 changes: 6 additions & 0 deletions jive_core/values/jive_Object.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@ namespace jive
const juce::var& newValue) override;
const juce::NamedValueSet& getProperties() const;

Object* getParent() noexcept;
const Object* getParent() const noexcept;
Object* getRoot() noexcept;
const Object* getRoot() const noexcept;

void addListener(Listener& listener) const;
void removeListener(Listener& listener) const;

Expand All @@ -35,6 +40,7 @@ namespace jive

mutable juce::ListenerList<Listener> listeners;
const std::unique_ptr<Listener> internalListener;
Object* parent = nullptr;

JUCE_LEAK_DETECTOR(Object)
};
Expand Down
Loading

0 comments on commit 9797773

Please sign in to comment.