conditions
- From: Paul Pogonyshev <pogonyshev gmx net>
- To: gtkmm-list gnome org
- Subject: conditions
- Date: Tue, 11 Jul 2006 19:41:46 +0300
Hi,
I'd like to propose the following extension to Gtkmm.
Currently, all non-trivial programs do something like this many times:
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 ();
...
void
update_sensitivity ()
{
button->set_sensitive (entry->get_text_length () > 0 && check_button->get_active ());
}
With conditions it can be simplified to
button->set_sensitive (entry->get_not_empty_condition () & check_button->get_active_condition ());
This gives two advantages: 1) less coding, and 2) less error probability
since you don't have to keep in sync state computation and change tracking,
it is done automatically. (I.e. in the example above update_sensitivity()
function must be connected to appropriate signals and if the function itself
changes, the signal set might change too; with conditions this is done
automatically.)
Downsides:
- Gtkmm must support Gtk::Widget::set_sensitive (condition) methods (few
methods, like set_sensitive(), set_visible() etc.) and, more importantly,
standard conditions like Gtk::ToggleButton::get_actve_conditions() (more
methods in various classes.) This also involves a few fields for each
widget class for an efficient implementation.
- Somewhat less efficient than standard approach, but this is not important
in GUI.
I have a working example of this, which is attached. Note that I couldn't
build methods into Gtkmm, so condition creation is more ugly in the
example. Instead of the one line above it is
controller = new SensivityController (button,
(condition (new EntryNotEmptyCondition (entry))
& condition (new ToggleButtonActiveCondition (check_button))));
In case you are interested, I can implement it in Gtkmm.
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]