Re: [sigc] conditions in libsigc



Murray Cumming wrote:
> On Fri, 2006-07-07 at 22:42 +0300, Paul Pogonyshev wrote:
> > Currently, you have to 1) set sensitivity (some state) to a computed value;
> > 2) track when that computed value changes its state, by connecting to the
> > appropriate callbacks; if the rules change, you have to not forget to keep
> > the computation and state tracking in sync; and 3) set the initial state
> > (sometimes).  Note that the initial state should also be in sync with
> > points 1) and 2).
> > 
> > I propose to do all the steps at once, using `condition's.  This roughly
> > corresponds to step 1), while all the rest is done automatically.
> > 
> > Using conditions (doesn't work currently, of course; how it should look
> > like):
> > 
> >     button->set_sensitive (entry->get_not_empty_condition () & check_button->get_active_condition ());
> 
> This is an interesting idea, but I have no idea how you might implement
> it.

I posted an implementation here and repost it.  Note that conditions and
sensivity statement in the example is not so nice, because there is no
(yet) support on the part of Gtkmm.  It looks like this:

    controller = new SensivityController (button,
					  (condition (new EntryNotEmptyCondition (entry))
					   & condition (new ToggleButtonActiveCondition (check_button))));

Otherwise, the example is fully working, though not optimized etc.  In
particular, all three steps are encapsulated with creating conditions
and nothing else.

I want to note that it doesn't require much from sigc++.  It is a small
new module, probably some 1000 lines in one file.  And it doesn't
influence the existing functionality in any way.

Paul


#include <sigc++/signal.h>
#include <gtkmm.h>


// sigc++ part.

class condition;


class condition_impl : public sigc::trackable
{
  friend  class condition; 


  bool				  state;
  int				  reference_counter;
  sigc::signal <void, condition>  state_changed;


  void
  reference ()
  {
    ++reference_counter;
  }

  bool
  unreference ()
  {
    if (!--reference_counter)
      delete this;
  }


protected:

  explicit
  condition_impl (bool initial_state)
    : state		(initial_state),
      reference_counter (0)
  { }

  virtual
  ~condition_impl ()
  { }

  void  set_state (bool new_state);
};


class condition
{
  condition_impl*  implementation;


public:

  explicit
  condition (condition_impl* implementation)
    : implementation (implementation)
  {
    implementation->reference ();
  }

  condition (const condition& other_condition)
    : implementation (other_condition.implementation)
  {
    implementation->reference ();
  }

  ~condition ()
  {
    implementation->unreference ();
  }


  operator bool () const
  {
    return implementation->state;
  }

  sigc::signal <void, condition>&
  signal_state_changed ()
  {
    return implementation->state_changed;
  }
};


class conjunction : public condition_impl
{
  const condition  first;
  const condition  second;


public:

  conjunction (condition first, condition second);


private:

  void  update (condition condition);
};


void
condition_impl::set_state (bool new_state)
{
  if (state != new_state)
    {
      state = new_state;
      state_changed (condition (this));
    }
}


conjunction::conjunction (condition first, condition second)
  : condition_impl (first && second),
    first	   (first),
    second	   (second)
{
  first.signal_state_changed  ().connect (sigc::mem_fun (*this, &conjunction::update));
  second.signal_state_changed ().connect (sigc::mem_fun (*this, &conjunction::update));
}


inline  condition
operator& (condition first, condition second)
{
  return condition (new conjunction (first, second));
}


void
conjunction::update (condition condition)
{
  set_state (first && second);
}


// Gtkmm part.

class EntryNotEmptyCondition : public condition_impl
{
  const Glib::RefPtr <Gtk::Entry>  entry;


public:

  explicit
  EntryNotEmptyCondition (Gtk::Entry* entry)
    : condition_impl (entry->get_text_length () > 0),
      entry	     (entry)
  {
    entry->signal_changed ().connect (sigc::mem_fun (*this, &EntryNotEmptyCondition::update_condition));
  }


  void
  update_condition ()
  {
    set_state (entry->get_text_length () > 0);
  }
};


class ToggleButtonActiveCondition : public condition_impl
{
  const Glib::RefPtr <Gtk::ToggleButton>  toggle_button;


public:

  explicit
  ToggleButtonActiveCondition (Gtk::ToggleButton* toggle_button)
    : condition_impl (toggle_button->get_active ()),
      toggle_button  (toggle_button)
  {
    toggle_button->signal_toggled ().connect (sigc::mem_fun (*this, &ToggleButtonActiveCondition::update_condition));
  }


  void
  update_condition ()
  {
    set_state (toggle_button->get_active ());
  }
};


class SensivityController
{
  Glib::RefPtr <Gtk::Widget>  widget;
  condition		      sensitivity;


public:

  SensivityController (Gtk::Widget* widget, condition sensitivity)
    : widget      (widget),
      sensitivity (sensitivity)
  {
    sensitivity.signal_state_changed ().connect
      (sigc::mem_fun (*this, &SensivityController::update_sensivity));
    update_sensivity (sensitivity);
  }


  void
  update_sensivity (condition condition)
  {
    widget->set_sensitive (condition);
  }
};


// Program part.

class Windows
{
  Gtk::Window*          window;
  Gtk::VBox*	        vbox;
  Gtk::Entry*           entry;
  Gtk::CheckButton*     check_button;
  Gtk::Button*          button;
  SensivityController*  controller;


public:

  Windows ()
  {
    window       = new Gtk::Window ();
    vbox	 = manage (new Gtk::VBox (false, 12));
    entry	 = manage (new Gtk::Entry ());
    check_button = manage (new Gtk::CheckButton ("Check me"));
    button       = manage (new Gtk::Button (Gtk::Stock::CLOSE));

    window->add (*vbox);
    vbox->add (*entry);
    vbox->add (*check_button);
    vbox->add (*button);

    window->set_title ("With Conditions");

    controller = new SensivityController (button,
					  (condition (new EntryNotEmptyCondition (entry))
					   & condition (new ToggleButtonActiveCondition (check_button))));

    window->show_all ();

    window       = new Gtk::Window ();
    vbox	 = manage (new Gtk::VBox (false, 12));
    entry	 = manage (new Gtk::Entry ());
    check_button = manage (new Gtk::CheckButton ("Check me"));
    button       = manage (new Gtk::Button (Gtk::Stock::CLOSE));

    window->add (*vbox);
    vbox->add (*entry);
    vbox->add (*check_button);
    vbox->add (*button);

    window->set_title ("Without Conditions");

    entry->signal_changed ().connect (sigc::mem_fun (*this, &Windows::update_sensitivity));
    check_button->signal_toggled ().connect (sigc::mem_fun (*this, &Windows::update_sensitivity));

    // Set initial state.
    update_sensitivity ();

    window->show_all ();
  }


private:

  void
  update_sensitivity ()
  {
    button->set_sensitive (entry->get_text_length () > 0 && check_button->get_active ());
  }
};


int
main (int argc, char** argv)
{
  Gtk::Main  main_loop (argc, argv);
  Windows    windows;

  main_loop.run ();

  return 0;
}


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]