texus / TGUI

Cross-platform modern c++ GUI

Home Page:https://tgui.eu

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Custom Callbacks for Widgets?

colesnicov opened this issue · comments

Hi. Is there a possibility to automatically call a custom function (callback) before drawing the widget itself? Need if I want to change the text on the button before each rendering, for example ...

Example:

void my_func (Widget & widget) {
  // Here, for example, the color of the button changes every second
  if (ellapsedTime> 1000) {
    widget.setColor (...);
  }
}

// Here, for example, callback binding
my_button.beforeDraw = & my_func;

The code is, of course, illustrative only, to understand the query.

Thanks for the advice ..

Is there any reason why you can't just call my_func before calling gui.draw()?

I don't want to do it in the main loop. I would like to attach a function to the widget that will make the necessary changes to itself (widget) before rendering each of the widgets and will do it discreetly. I like to write code based on callback ...

The function should accept as an argument the widget object that calls it. Something like that?

I have a lot of widgets..

There is no such callback. The signal system supports functions with any parameters, so if you only needed it for widgets of a single type then you could inherit from it and write your custom widget type that does send such callbacks, but you can't add things to all widgets irrelevant of their type.

You can use the Timer class to get a callback every second, but then you still need to loop over widgets inside that callback.
Timer::create(&your_func, 1000)

If you only need to change the renderer of many widgets, then maybe you shouldn't be looping over them. Instead you should just change the renderer to change all widgets of that type at once (assuming the other widgets where loaded from the same theme and getRenderer() wasn't called on the widgets yet).
tgui::ButtonRenderer(theme.getRenderer("Button")).setBackgroundColor(sf::Color::Blue);

Without knowing the details of what you are trying to do it is hard to suggest alternatives, but there exists no callback that is called every frame so you will have to take some different approach anyway.

Well, imagine I have 30 different text elements on the screen. Each of these elements dynamically displays text. The text on the individual 'Labels' changes at different intervals independently of each other.
To make matters worse, these 'Labels' are created dynamically and therefore, I would rather put a function on each 'Label' that verifies the time and makes changes. Sure, I can do it differently .. I don't want to burden, it's just a question... Maybe a feature...

It definitely is something to think about. If I find a good enough use case for it in the future then I might still add it, although I would probably call it onUpdate instead of onDraw.

I considered adding it for a moment because looping over widgets is complicated: there are containers and you have to loop over their child widgets as well. So it would make sense if there was a simpler way. But you don't really want to loop over ALL widgets, you know precisely which widgets you need to update (e.g. you wouldn't connect onUpdate on every widget). So all you need are really these lines of code:

std::map<tgui::Widget::Ptr, std::function<void(tgui::Widget::Ptr)>> callbacks; // Have this somewhere
    
callbacks[label] = &my_func; // Call when label is created

callbacks.erase(label); // Call when label is destroyed

// Call in main loop
for (auto& [w, f] : callbacks)
    f(w);

That's less code than it would take me to implement an onUpdate function inside TGUI. That's why I'm being so reluctant to add it.

I do it like this for now, pseudo:

class widget{
  public:
  virtual void update() = 0;
}

class custom_widget: public widget {
  public:
   // This function can manipulate this objects
   void update() {
     label.setText(...); // Set new text
   }
  
   private:
   tgui::Label::Ptr label;
};

class Container {
  public:
 
  void add(widget* w){
    ....
    count++;
  }

  void update() {
    for(i, i < count; i++) {
      wgts[i]->update(); // Update every widget ...
    }
  }

  private:
  widget *wgts[];
  int count;
}


void main() {
  Container cnt;
  custom_widget *w1, *w2;

  cnt.add(w1);
  cnt.add(w2);
  while(1) {
    cnt.update(); // Update container 
    tgui.draw();
  }
}

Updated