Changed mechanism of Source Callback and Input Mapping

Session stores list of all callback instances and reacts on input release by calling the reverse callback if it exists, or by finishing the ongoing callback. This means the behavior of Callbacks is different for those who are reversible (i.e. returns a non-null reverse) from those which do not have reverse. The reversible callbacks enforce to be exclusive while active (key pressed), others can be repeated and complementary (run in parallel).
This commit is contained in:
Bruno Herbelin
2023-02-09 23:18:24 +01:00
parent 7433772606
commit 128e8834e8
8 changed files with 103 additions and 165 deletions

View File

@@ -632,7 +632,7 @@ bool Control::receiveSourceAttribute(Source *target, const std::string &attribut
else if ( attribute.compare(OSC_SOURCE_LOOM) == 0) { else if ( attribute.compare(OSC_SOURCE_LOOM) == 0) {
float x = 1.f; float x = 1.f;
arguments >> x >> osc::EndMessage; arguments >> x >> osc::EndMessage;
target->call( new Loom(x, 0.f), true ); target->call( new Loom(x, 0.f) );
// this will require to send feedback status about source // this will require to send feedback status about source
send_feedback = true; send_feedback = true;
} }
@@ -662,7 +662,7 @@ bool Control::receiveSourceAttribute(Source *target, const std::string &attribut
catch (osc::WrongArgumentTypeException &) { catch (osc::WrongArgumentTypeException &) {
} }
arguments >> osc::EndMessage; arguments >> osc::EndMessage;
target->call( new Grab( x, y, 0.f), true ); target->call( new Grab( x, y, 0.f) );
} }
/// e.g. '/vimix/current/position ff 10.0 2.2' /// e.g. '/vimix/current/position ff 10.0 2.2'
else if ( attribute.compare(OSC_SOURCE_POSITION) == 0) { else if ( attribute.compare(OSC_SOURCE_POSITION) == 0) {
@@ -699,7 +699,7 @@ bool Control::receiveSourceAttribute(Source *target, const std::string &attribut
catch (osc::WrongArgumentTypeException &) { catch (osc::WrongArgumentTypeException &) {
} }
arguments >> osc::EndMessage; arguments >> osc::EndMessage;
target->call( new Resize( x, y, 0.f), true ); target->call( new Resize( x, y, 0.f) );
} }
/// e.g. '/vimix/current/size ff 1.0 2.2' /// e.g. '/vimix/current/size ff 1.0 2.2'
else if ( attribute.compare(OSC_SOURCE_SIZE) == 0) { else if ( attribute.compare(OSC_SOURCE_SIZE) == 0) {
@@ -730,7 +730,7 @@ bool Control::receiveSourceAttribute(Source *target, const std::string &attribut
arguments >> osc::EndMessage; arguments >> osc::EndMessage;
else else
arguments >> t >> osc::EndMessage; arguments >> t >> osc::EndMessage;
target->call( new Turn( x, t), true ); target->call( new Turn( x, t) );
} }
/// e.g. '/vimix/current/angle f 3.1416' /// e.g. '/vimix/current/angle f 3.1416'
else if ( attribute.compare(OSC_SOURCE_ANGLE) == 0) { else if ( attribute.compare(OSC_SOURCE_ANGLE) == 0) {

View File

@@ -76,13 +76,18 @@ Session::Session(uint64_t id) : id_(id), active_(true), activation_threshold_(MI
} }
void Session::InputSourceCallback::clearReverse() void Session::InputSourceCallback::clear()
{ {
for (auto rev = reverse_.begin(); rev != reverse_.end(); ++rev) { // go through all instances stored in InputSourceCallback
if (rev->second != nullptr) for (auto clb = instances_.begin(); clb != instances_.end(); ++clb) {
delete rev->second; // finish all
if (clb->second.first != nullptr)
clb->second.first->finish();
if (clb->second.second != nullptr)
clb->second.second->finish();
} }
reverse_.clear(); // do not keep references: will be deleted when terminated
instances_.clear();
} }
Session::~Session() Session::~Session()
@@ -105,7 +110,7 @@ Session::~Session()
iter = input_callbacks_.erase(iter)) { iter = input_callbacks_.erase(iter)) {
if ( iter->second.model_ != nullptr) if ( iter->second.model_ != nullptr)
delete iter->second.model_; delete iter->second.model_;
iter->second.clearReverse(); iter->second.clear();
} }
delete config_[View::RENDERING]; delete config_[View::RENDERING];
@@ -151,13 +156,10 @@ void Session::update(float dt)
if ( k->second.model_ != nullptr && k->second.target_.index() > 0) { if ( k->second.model_ != nullptr && k->second.target_.index() > 0) {
// if the value referenced as pressed changed state // if the value referenced as pressed changed state
// or repeat key if there is no reverse callback if ( input_active != k->second.active_ ) {
if ( input_active != k->second.active_ || k->second.reverse_.empty()) {
// ON PRESS // ON PRESS
if (input_active) { if (input_active) {
// delete the reverse if was not released
k->second.clearReverse();
// Add callback to the target(s) // Add callback to the target(s)
// 1. Case of variant as Source pointer // 1. Case of variant as Source pointer
@@ -165,15 +167,18 @@ void Session::update(float dt)
// verify variant value // verify variant value
if ( *v != nullptr ) { if ( *v != nullptr ) {
// generate a new callback from the model // generate a new callback from the model
SourceCallback *C = k->second.model_->clone(); SourceCallback *forward = k->second.model_->clone();
// apply value multiplyer from input // apply value multiplyer from input
C->multiply( Control::manager().inputValue(k->first) ); forward->multiply( Control::manager().inputValue(k->first) );
// add delay // add delay
C->delay( Metronome::manager().timeToSync( (Metronome::Synchronicity) input_sync_[k->first] ) ); forward->delay( Metronome::manager().timeToSync( (Metronome::Synchronicity) input_sync_[k->first] ) );
// add callback to source // add callback to source
(*v)->call( C, true ); (*v)->call( forward );
// get the reverse if the callback, and remember it (can be null) // get the reverse of the callback (can be null)
k->second.reverse_[(*v)->id()] = C->reverse(*v); SourceCallback *backward = forward->reverse(*v);;
// remember instances
k->second.instances_[(*v)->id()] = {forward, backward};
} }
} }
// 2. Case of variant as index of batch // 2. Case of variant as index of batch
@@ -186,15 +191,17 @@ void Session::update(float dt)
SourceList::const_iterator sit = std::find_if(sources_.begin(), sources_.end(), Source::hasId(*sid));; SourceList::const_iterator sit = std::find_if(sources_.begin(), sources_.end(), Source::hasId(*sid));;
if ( sit != sources_.end()) { if ( sit != sources_.end()) {
// generate a new callback from the model // generate a new callback from the model
SourceCallback *C = k->second.model_->clone(); SourceCallback *forward = k->second.model_->clone();
// apply value multiplyer from input // apply value multiplyer from input
C->multiply( Control::manager().inputValue(k->first) ); forward->multiply( Control::manager().inputValue(k->first) );
// add delay // add delay
C->delay( Metronome::manager().timeToSync( (Metronome::Synchronicity) input_sync_[k->first] ) ); forward->delay( Metronome::manager().timeToSync( (Metronome::Synchronicity) input_sync_[k->first] ) );
// add callback to source // add callback to source
(*sit)->call( C, true ); (*sit)->call( forward );
// get the reverse if the callback, and remember it (can be null) // get the reverse of the callback (can be null)
k->second.reverse_[*sid] = C->reverse(*sit); SourceCallback *backward = forward->reverse(*sit);;
// remember instances
k->second.instances_[*sid] = {forward, backward};
} }
} }
} }
@@ -202,15 +209,22 @@ void Session::update(float dt)
} }
// ON RELEASE // ON RELEASE
else { else {
// call all the reverse of sources (NB: invalid value tested in call) // go through all instances stored for that action
for (auto rev = k->second.reverse_.begin(); rev != k->second.reverse_.end(); ++rev) { for (auto clb = k->second.instances_.begin(); clb != k->second.instances_.end(); ++clb) {
SourceList::const_iterator sit = std::find_if(sources_.begin(), sources_.end(), Source::hasId(rev->first));; // find the source referenced by each instance
SourceList::const_iterator sit = std::find_if(sources_.begin(), sources_.end(), Source::hasId(clb->first));;
// if the source is valid
if ( sit != sources_.end()) { if ( sit != sources_.end()) {
(*sit)->call( rev->second, true ); // either call the reverse if exists (stored as second element in pair)
if (clb->second.second != nullptr)
(*sit)->call( clb->second.second, true );
// or finish the called
else
(*sit)->finish( clb->second.first );
} }
} }
// do not keep reference to reverse: will be deleted when terminated // do not keep reference to instances: will be deleted when finished
k->second.reverse_.clear(); k->second.instances_.clear();
} }
// remember state of callback // remember state of callback
@@ -779,7 +793,7 @@ void Session::assignInputCallback(uint input, Target target, SourceCallback *cal
if ( k->second.model_ == callback) { if ( k->second.model_ == callback) {
k->second.target_ = target; k->second.target_ = target;
// reverse became invalid // reverse became invalid
k->second.clearReverse(); k->second.clear();
break; break;
} }
} }
@@ -837,7 +851,7 @@ void Session::deleteInputCallback(SourceCallback *callback)
{ {
if ( k->second.model_ == callback) { if ( k->second.model_ == callback) {
delete callback; delete callback;
k->second.clearReverse(); k->second.clear();
input_callbacks_.erase(k); input_callbacks_.erase(k);
break; break;
} }
@@ -851,7 +865,7 @@ void Session::deleteInputCallbacks(uint input)
if ( k->first == input) { if ( k->first == input) {
if (k->second.model_) if (k->second.model_)
delete k->second.model_; delete k->second.model_;
k->second.clearReverse(); k->second.clear();
k = input_callbacks_.erase(k); k = input_callbacks_.erase(k);
} }
else else
@@ -866,7 +880,7 @@ void Session::deleteInputCallbacks(Target target)
if ( k->second.target_ == target) { if ( k->second.target_ == target) {
if (k->second.model_) if (k->second.model_)
delete k->second.model_; delete k->second.model_;
k->second.clearReverse(); k->second.clear();
k = input_callbacks_.erase(k); k = input_callbacks_.erase(k);
} }
else else
@@ -881,7 +895,7 @@ void Session::clearInputCallbacks()
{ {
if (k->second.model_) if (k->second.model_)
delete k->second.model_; delete k->second.model_;
k->second.clearReverse(); k->second.clear();
k = input_callbacks_.erase(k); k = input_callbacks_.erase(k);
} }

View File

@@ -217,14 +217,14 @@ protected:
struct InputSourceCallback { struct InputSourceCallback {
bool active_; bool active_;
SourceCallback *model_; SourceCallback *model_;
std::map<uint64_t, SourceCallback *> reverse_; std::map<uint64_t, std::pair< SourceCallback *, SourceCallback *> > instances_;
Target target_; Target target_;
InputSourceCallback() { InputSourceCallback() {
active_ = false; active_ = false;
model_ = nullptr; model_ = nullptr;
target_ = nullptr; target_ = nullptr;
} }
void clearReverse(); void clear();
}; };
std::multimap<uint, InputSourceCallback> input_callbacks_; std::multimap<uint, InputSourceCallback> input_callbacks_;
std::vector<Metronome::Synchronicity> input_sync_; std::vector<Metronome::Synchronicity> input_sync_;

View File

@@ -655,38 +655,17 @@ void Source::call(SourceCallback *callback, bool override)
// lock access to callbacks list // lock access to callbacks list
access_callbacks_.lock(); access_callbacks_.lock();
bool add = true; // if operation should override previous callbacks of same type
if (override) {
// look for callbacks of same type // finish all callbacks of the same type
for (auto iter=update_callbacks_.begin(); iter != update_callbacks_.end(); ) for (auto iter=update_callbacks_.begin(); iter != update_callbacks_.end(); ++iter) {
{ if ( callback->type() == (*iter)->type() )
// Test if the new callback would overlap an existing one (*iter)->finish();
SourceCallback *c = *iter;
if ( SourceCallback::overlap( callback, c) ) {
if (override) {
// either remove and delete all overlapping callbacks if override...
iter = update_callbacks_.erase(iter);
delete c;
}
else {
// ...or cancel adding overlapping callbacks if not override
add = false;
break;
}
} }
// iterate
else
++iter;
} }
// we can add the callback : its either not overlapping or we override it // allways add the given callback to list of callbacks
if (add) { update_callbacks_.push_back(callback);
// add callback to callbacks list
update_callbacks_.push_back(callback);
}
// or delete it if couln't be added (overlapping but not override)
else
delete callback;
// release access to callbacks list // release access to callbacks list
access_callbacks_.unlock(); access_callbacks_.unlock();
@@ -694,10 +673,29 @@ void Source::call(SourceCallback *callback, bool override)
} }
void Source::finish(SourceCallback *callback)
{
if (callback != nullptr) {
// lock access to callbacks list
access_callbacks_.lock();
// make sure the given pointer is a callback listed in this source
auto cb = std::find(update_callbacks_.begin(), update_callbacks_.end(), callback);
if (cb != update_callbacks_.end())
// set found callback to finish state
(*cb)->finish();
// release access to callbacks list
access_callbacks_.unlock();
}
}
void Source::updateCallbacks(float dt) void Source::updateCallbacks(float dt)
{ {
// lock access to callbacks list // lock access to callbacks list
access_callbacks_.lock(); access_callbacks_.lock();
// call callback functions // call callback functions
for (auto iter=update_callbacks_.begin(); iter != update_callbacks_.end(); ) for (auto iter=update_callbacks_.begin(); iter != update_callbacks_.end(); )
{ {
@@ -716,9 +714,9 @@ void Source::updateCallbacks(float dt)
else else
++iter; ++iter;
} }
// release access to callbacks list // release access to callbacks list
access_callbacks_.unlock(); access_callbacks_.unlock();
} }
CloneSource *Source::clone(uint64_t id) CloneSource *Source::clone(uint64_t id)

View File

@@ -149,6 +149,7 @@ public:
// add callback to each update // add callback to each update
void call(SourceCallback *callback, bool override = false); void call(SourceCallback *callback, bool override = false);
void finish(SourceCallback *callback);
// update mode // update mode
inline bool active () const { return active_; } inline bool active () const { return active_; }

View File

@@ -22,9 +22,7 @@
#include "ImageProcessingShader.h" #include "ImageProcessingShader.h"
#include "MediaSource.h" #include "MediaSource.h"
#include "MediaPlayer.h" #include "MediaPlayer.h"
#include "UpdateCallback.h"
#include "Visitor.h" #include "Visitor.h"
#include "Log.h"
#include "SourceCallback.h" #include "SourceCallback.h"
@@ -101,53 +99,6 @@ SourceCallback *SourceCallback::create(CallbackType type)
return loadedcallback; return loadedcallback;
} }
bool SourceCallback::overlap( SourceCallback *a, SourceCallback *b)
{
bool ret = false;
if (a->type() == b->type()) {
// same type means overlap
ret = true;
// but there are some exceptions..
switch (a->type()) {
case SourceCallback::CALLBACK_GRAB:
{
const Grab *_a = static_cast<Grab*>(a);
const Grab *_b = static_cast<Grab*>(b);
if ( ABS_DIFF(_a->value().x, _b->value().x) > EPSILON || ABS_DIFF(_a->value().y, _b->value().y) > EPSILON )
ret = false;
// // there is no overlap if the X or Y of a vector is zero
// if ( ABS(_a->value().x) < EPSILON || ABS(_b->value().x) < EPSILON )
// ret = false;
// else if ( ABS(_a->value().y) < EPSILON || ABS(_b->value().y) < EPSILON )
// ret = false;
}
break;
case SourceCallback::CALLBACK_RESIZE:
{
const Resize *_a = static_cast<Resize*>(a);
const Resize *_b = static_cast<Resize*>(b);
if ( ABS_DIFF(_a->value().x, _b->value().x) > EPSILON || ABS_DIFF(_a->value().y, _b->value().y) > EPSILON )
ret = false;
// if ( ABS(_a->value().x) < EPSILON || ABS(_b->value().x) < EPSILON )
// ret = false;
// else if ( ABS(_a->value().y) < EPSILON || ABS(_b->value().y) < EPSILON )
// ret = false;
}
break;
default:
break;
}
}
return ret;
}
SourceCallback::SourceCallback(): status_(PENDING), delay_(0.f), elapsed_(0.f) SourceCallback::SourceCallback(): status_(PENDING), delay_(0.f), elapsed_(0.f)
{ {
} }
@@ -384,7 +335,6 @@ SourceCallback *Lock::clone() const
Loom::Loom(float speed, float ms) : SourceCallback(), Loom::Loom(float speed, float ms) : SourceCallback(),
speed_(speed), duration_(ms) speed_(speed), duration_(ms)
{ {
pos_ = glm::vec2();
step_ = glm::normalize(glm::vec2(1.f, 1.f)); // step in diagonal by default step_ = glm::normalize(glm::vec2(1.f, 1.f)); // step in diagonal by default
} }
@@ -395,13 +345,14 @@ void Loom::update(Source *s, float dt)
if (s->locked()) if (s->locked())
status_ = FINISHED; status_ = FINISHED;
// current position
glm::vec2 pos = glm::vec2(s->group(View::MIXING)->translation_);
// set start on first time it is ready // set start on first time it is ready
if ( status_ == READY ){ if ( status_ == READY ){
// initial position
pos_ = glm::vec2(s->group(View::MIXING)->translation_);
// step in direction of source translation if possible // step in direction of source translation if possible
if ( glm::length(pos_) > DELTA_ALPHA) if ( glm::length(pos) > DELTA_ALPHA)
step_ = glm::normalize(pos_); step_ = glm::normalize(pos);
status_ = ACTIVE; status_ = ACTIVE;
} }
@@ -410,12 +361,12 @@ void Loom::update(Source *s, float dt)
float progress = elapsed_ - delay_; float progress = elapsed_ - delay_;
// move target by speed vector (in the direction of step_, amplitude of speed * time (in second)) // move target by speed vector (in the direction of step_, amplitude of speed * time (in second))
pos_ -= step_ * ( speed_ * dt * 0.001f ); pos -= step_ * ( speed_ * dt * 0.001f );
// apply alpha if pos in range [0 MIXING_MIN_THRESHOLD] // apply alpha if pos in range [0 MIXING_MAX_THRESHOLD]
float l = glm::length( pos_ ); float l = glm::length( pos );
if ( (l > 0.01f && speed_ > 0.f ) || (l < MIXING_MIN_THRESHOLD && speed_ < 0.f ) ) if ( (l > 0.01f && speed_ > 0.f ) || (l < MIXING_MAX_THRESHOLD && speed_ < 0.f ) )
s->group(View::MIXING)->translation_ = glm::vec3(pos_, s->group(View::MIXING)->translation_.z); s->group(View::MIXING)->translation_ = glm::vec3(pos, s->group(View::MIXING)->translation_.z);
else else
status_ = FINISHED; status_ = FINISHED;
@@ -436,11 +387,6 @@ SourceCallback *Loom::clone() const
return new Loom(speed_, duration_); return new Loom(speed_, duration_);
} }
SourceCallback *Loom::reverse(Source *) const
{
return new Loom(speed_, 0.f);
}
void Loom::accept(Visitor& v) void Loom::accept(Visitor& v)
{ {
SourceCallback::accept(v); SourceCallback::accept(v);
@@ -696,15 +642,15 @@ void Grab::update(Source *s, float dt)
// set start on first time it is ready // set start on first time it is ready
if ( status_ == READY ) { if ( status_ == READY ) {
// initial position // initial position
pos_ = glm::vec2(s->group(View::GEOMETRY)->translation_);
status_ = ACTIVE; status_ = ACTIVE;
} }
if ( status_ == ACTIVE ) { if ( status_ == ACTIVE ) {
// move target by speed vector * time (in second) // move target by speed vector * time (in second)
pos_ += speed_ * ( dt * 0.001f); glm::vec2 pos = glm::vec2(s->group(View::GEOMETRY)->translation_);
s->group(View::GEOMETRY)->translation_ = glm::vec3(pos_, s->group(View::GEOMETRY)->translation_.z); pos += speed_ * ( dt * 0.001f);
s->group(View::GEOMETRY)->translation_ = glm::vec3(pos, s->group(View::GEOMETRY)->translation_.z);
// time-out // time-out
if ( (elapsed_ - delay_) > duration_ ) if ( (elapsed_ - delay_) > duration_ )
@@ -723,11 +669,6 @@ SourceCallback *Grab::clone() const
return new Grab(speed_.x, speed_.y, duration_); return new Grab(speed_.x, speed_.y, duration_);
} }
SourceCallback *Grab::reverse(Source *) const
{
return new Grab(speed_.x, speed_.y, 0.f);
}
void Grab::accept(Visitor& v) void Grab::accept(Visitor& v)
{ {
SourceCallback::accept(v); SourceCallback::accept(v);
@@ -770,11 +711,6 @@ SourceCallback *Resize::clone() const
return new Resize(speed_.x, speed_.y, duration_); return new Resize(speed_.x, speed_.y, duration_);
} }
SourceCallback *Resize::reverse(Source *) const
{
return new Resize(speed_.x, speed_.y, 0.f);
}
void Resize::accept(Visitor& v) void Resize::accept(Visitor& v)
{ {
SourceCallback::accept(v); SourceCallback::accept(v);
@@ -795,16 +731,17 @@ void Turn::update(Source *s, float dt)
// set start on first time it is ready // set start on first time it is ready
if ( status_ == READY ){ if ( status_ == READY ){
// initial position
angle_ = s->group(View::GEOMETRY)->rotation_.z;
status_ = ACTIVE; status_ = ACTIVE;
} }
if ( status_ == ACTIVE ) { if ( status_ == ACTIVE ) {
// current position
float angle = s->group(View::GEOMETRY)->rotation_.z;
// perform movement // perform movement
angle_ -= speed_ * ( dt * 0.001f ); angle -= speed_ * ( dt * 0.001f );
s->group(View::GEOMETRY)->rotation_.z = angle_; s->group(View::GEOMETRY)->rotation_.z = angle;
// timeout // timeout
if ( (elapsed_ - delay_) > duration_ ) if ( (elapsed_ - delay_) > duration_ )
@@ -823,11 +760,6 @@ SourceCallback *Turn::clone() const
return new Turn(speed_, duration_); return new Turn(speed_, duration_);
} }
SourceCallback *Turn::reverse(Source *) const
{
return new Turn(speed_, 0.f);
}
void Turn::accept(Visitor& v) void Turn::accept(Visitor& v)
{ {
SourceCallback::accept(v); SourceCallback::accept(v);

View File

@@ -50,7 +50,6 @@ public:
} CallbackType; } CallbackType;
static SourceCallback *create(CallbackType type); static SourceCallback *create(CallbackType type);
static bool overlap(SourceCallback *a, SourceCallback *b);
SourceCallback(); SourceCallback();
virtual ~SourceCallback() {} virtual ~SourceCallback() {}
@@ -62,8 +61,9 @@ public:
virtual CallbackType type () const { return CALLBACK_GENERIC; } virtual CallbackType type () const { return CALLBACK_GENERIC; }
virtual void accept (Visitor& v); virtual void accept (Visitor& v);
inline bool finished () const { return status_ > ACTIVE; }
inline void reset () { status_ = PENDING; } inline void reset () { status_ = PENDING; }
inline void finish () { status_ = FINISHED; }
inline bool finished () const { return status_ > ACTIVE; }
inline void delay (float milisec) { delay_ = milisec;} inline void delay (float milisec) { delay_ = milisec;}
protected: protected:
@@ -146,7 +146,6 @@ public:
class Loom : public SourceCallback class Loom : public SourceCallback
{ {
float speed_; float speed_;
glm::vec2 pos_;
glm::vec2 step_; glm::vec2 step_;
float duration_; float duration_;
@@ -161,7 +160,6 @@ public:
void update (Source *s, float) override; void update (Source *s, float) override;
void multiply (float factor) override; void multiply (float factor) override;
SourceCallback *clone() const override; SourceCallback *clone() const override;
SourceCallback *reverse(Source *) const override;
CallbackType type () const override { return CALLBACK_LOOM; } CallbackType type () const override { return CALLBACK_LOOM; }
void accept (Visitor& v) override; void accept (Visitor& v) override;
}; };
@@ -273,7 +271,6 @@ public:
class Grab : public SourceCallback class Grab : public SourceCallback
{ {
glm::vec2 speed_; glm::vec2 speed_;
glm::vec2 pos_;
float duration_; float duration_;
public: public:
@@ -287,7 +284,6 @@ public:
void update (Source *s, float) override; void update (Source *s, float) override;
void multiply (float factor) override; void multiply (float factor) override;
SourceCallback *clone () const override; SourceCallback *clone () const override;
SourceCallback *reverse(Source *) const override;
CallbackType type () const override { return CALLBACK_GRAB; } CallbackType type () const override { return CALLBACK_GRAB; }
void accept (Visitor& v) override; void accept (Visitor& v) override;
}; };
@@ -308,7 +304,6 @@ public:
void update (Source *s, float) override; void update (Source *s, float) override;
void multiply (float factor) override; void multiply (float factor) override;
SourceCallback *clone () const override; SourceCallback *clone () const override;
SourceCallback *reverse(Source *) const override;
CallbackType type () const override { return CALLBACK_RESIZE; } CallbackType type () const override { return CALLBACK_RESIZE; }
void accept (Visitor& v) override; void accept (Visitor& v) override;
}; };
@@ -316,7 +311,6 @@ public:
class Turn : public SourceCallback class Turn : public SourceCallback
{ {
float speed_; float speed_;
float angle_;
float duration_; float duration_;
public: public:
@@ -330,7 +324,6 @@ public:
void update (Source *s, float) override; void update (Source *s, float) override;
void multiply (float factor) override; void multiply (float factor) override;
SourceCallback *clone () const override; SourceCallback *clone () const override;
SourceCallback *reverse(Source *) const override;
CallbackType type () const override { return CALLBACK_TURN; } CallbackType type () const override { return CALLBACK_TURN; }
void accept (Visitor& v) override; void accept (Visitor& v) override;
}; };

View File

@@ -1207,7 +1207,7 @@ void UserInterface::RenderMetrics(bool *p_open, int* p_corner, int *p_mode)
float v = s->alpha(); float v = s->alpha();
ImGui::SetNextItemWidth(rightalign); ImGui::SetNextItemWidth(rightalign);
if ( ImGui::DragFloat("Alpha", &v, 0.01f, 0.f, 1.f) ) if ( ImGui::DragFloat("Alpha", &v, 0.01f, 0.f, 1.f) )
s->call(new SetAlpha(v)); s->call(new SetAlpha(v), true);
if ( ImGui::IsItemDeactivatedAfterEdit() ) { if ( ImGui::IsItemDeactivatedAfterEdit() ) {
info << "Alpha " << std::fixed << std::setprecision(3) << v; info << "Alpha " << std::fixed << std::setprecision(3) << v;
Action::manager().store(info.str()); Action::manager().store(info.str());