millennium-dev team mailing list archive
-
millennium-dev team
-
Mailing list archive
-
Message #00221
[Merge] lp:~rafalcieslak256/millenniumduel/events into lp:millenniumduel
Rafał Cieślak has proposed merging lp:~rafalcieslak256/millenniumduel/events into lp:millenniumduel.
Requested reviews:
Millennium Developers (millennium-dev)
For more details, see:
https://code.launchpad.net/~rafalcieslak256/millenniumduel/events/+merge/178464
This branch introduces a more extensive event system.
The class CEvent basically stores a list of functions it has to call when it's method Happen() is used. To add a function to CEvent, use Subscribe(...), which accepts a variety of arguments, including lambdas, function pointers, and even a class pointer + member function pointer.
Widgets have a number of CEvents contained as public fields. The widgets themselves are making sure the events .Happen() on the appropriate occasion.
Some widgets subscribe to their own events. For example, containers subscribe to event_clicked that is managed by IDrawable, so that they can process the click and pass it to their children. (Note that they should never .Happen() their children's event_clicked, but use OnClicked(...) instead, so that the IDrawble can process the click. Generally, widgets should NEVER .Happen() an event that is not their own).
Of course, anyone else can also subscribe to events. For example,
button_quit.event_activated.Subscribe( quit_function );
makes perfect sense.
Why is this better than the current solution?
Firstly, it allows more than one subscriber per event. This was not possible because overwriting methods always disables the original one.
Secondly, this way one can subscribe to any widget's event without adding any extra code specific to that widget. For example, if one day, for some reason we wish to react when a WImage is pointed with the mouse pointer, no extra code overwriting OnMouseIn has to be added to the WImage - simply Subscribe(..) to the appropriate event.
Some custom widgets may provide their own events and manage them on their own. For example, WButton has event_activated, which Happens() whenever the button is successfully clicked. A text entry widget might manage a event_text_changed event, which would be triggered whenever the text inside of it gets changed.
--
https://code.launchpad.net/~rafalcieslak256/millenniumduel/events/+merge/178464
Your team Millennium Developers is requested to review the proposed merge of lp:~rafalcieslak256/millenniumduel/events into lp:millenniumduel.
=== added file 'src/CEvent.hpp'
--- src/CEvent.hpp 1970-01-01 00:00:00 +0000
+++ src/CEvent.hpp 2013-08-04 16:35:32 +0000
@@ -0,0 +1,48 @@
+#ifndef __CEVENT_HPP__
+#define __CEVENT_HPP__
+
+#include <list>
+#include <functional>
+
+template <typename...>
+class CEvent;
+
+template <typename T>
+class CEvent<T>{
+private:
+ std::list< std::function<void(T)> > subscribers;
+ std::list< std::function<void() > > deaf_subscribers;
+public:
+ void Subscribe( std::function<void(T)> f ) {
+ subscribers.push_back(f);
+ };
+ void Subscribe( std::function<void()> f ) {
+ deaf_subscribers.push_back(f);
+ };
+ template<class C>
+ void Subscribe( C* class_ptr, void (C::*member_ptr)(T)){
+ subscribers.push_back( std::bind(member_ptr,class_ptr,std::placeholders::_1) );
+ }
+ void Happen(T t){
+ for(auto f : subscribers) f(t);
+ for(auto f : deaf_subscribers) f();
+ }
+};
+
+template <>
+class CEvent<>{
+private:
+ std::list< std::function<void()> > subscribers;
+public:
+ void Subscribe( std::function<void()> f ) {
+ subscribers.push_back(f);
+ };
+ template<class C>
+ void Subscribe( C* class_ptr, void (C::*member_ptr)()){
+ subscribers.push_back( std::bind(member_ptr,class_ptr) );
+ }
+ void Happen(){
+ for(auto f : subscribers) f();
+ }
+};
+#endif //__CEVENT_HPP__
=== modified file 'src/IDrawable.cpp'
--- src/IDrawable.cpp 2013-08-04 11:05:30 +0000
+++ src/IDrawable.cpp 2013-08-04 16:35:32 +0000
@@ -86,10 +86,11 @@
if(p.x < 0 || p.y < 0 || p.x > _last_drawn_width || p.y > _last_drawn_height)
return;
- if(p.x <= _margin || p.y <= _margin || p.x > _last_drawn_width-_margin || p.y > _last_drawn_height-_margin)
- _OnMarginClicked();
- else
- _OnInnerClicked(SCoordinates(p.x-_margin,p.y-_margin));
+ if(p.x <= _margin || p.y <= _margin || p.x > _last_drawn_width-_margin || p.y > _last_drawn_height-_margin){
+ event_margin_clicked.Happen();
+ }else{
+ event_clicked.Happen(SCoordinates(p.x-_margin,p.y-_margin));
+ }
}
void IDrawable::OnMouseIn(){
@@ -99,7 +100,7 @@
pointed = false;
if(inner_pointed){
inner_pointed = false;
- _OnInnerMouseOut();
+ event_mouse_out.Happen();
}
}
void IDrawable::OnMouseMovedOver(SCoordinates p){
@@ -108,26 +109,16 @@
if(p.x <= _margin || p.y <= _margin || p.x > _last_drawn_width-_margin || p.y > _last_drawn_height-_margin){
// mouse is outside the inner part of the widget
if(inner_pointed){
- _OnInnerMouseOut();
+ event_mouse_out.Happen();
inner_pointed = false;
}
}else{
// mouse is inside the inner part of the widget
if(!inner_pointed){
- _OnInnerMouseIn();
+ event_mouse_in.Happen();
inner_pointed = true;
}
- _OnInnerMouseMovedOver(SCoordinates(p.x - _margin, p.y - _margin));
+ event_mouse_over.Happen(SCoordinates(p.x - _margin, p.y - _margin));
}
}
-
-void IDrawable::_OnInnerClicked(SCoordinates p)
-{
- _OnInnerClicked();
-}
-void IDrawable::_OnInnerClicked() {}
-void IDrawable::_OnMarginClicked() {}
-void IDrawable::_OnInnerMouseIn() {}
-void IDrawable::_OnInnerMouseOut() {}
-void IDrawable::_OnInnerMouseMovedOver(SCoordinates) {}
=== modified file 'src/IDrawable.hpp'
--- src/IDrawable.hpp 2013-08-04 11:05:30 +0000
+++ src/IDrawable.hpp 2013-08-04 16:35:32 +0000
@@ -1,6 +1,7 @@
#ifndef __IDRAWABLE_HPP__
#define __IDRAWABLE_HPP__
+#include "CEvent.hpp"
#include <allegro5/allegro.h>
struct SDrawArea{
@@ -50,6 +51,15 @@
void OnMouseIn();
void OnMouseOut();
+ /* These events should NEVER be triggered by anyone outside this class, including
+ * all custom widgets! If you wish to notify a widget about such event, use
+ * appropriate On* methods above */
+ CEvent<SCoordinates> event_clicked;
+ CEvent<> event_margin_clicked;
+ CEvent<SCoordinates> event_mouse_over;
+ CEvent<> event_mouse_in;
+ CEvent<> event_mouse_out;
+
/* This variable is used to remember whether the mouse is currently over this
* widget */
bool pointed = false;
@@ -94,21 +104,6 @@
* new widget */
virtual void _Redraw(int w, int h) = 0;
- /* These methods should specify widget's reaction for click respectively on
- * it's interior or margin.
- * These are the methods you should override when creating a custom widget. */
- virtual void _OnInnerClicked(SCoordinates p);
- virtual void _OnInnerClicked();
- virtual void _OnMarginClicked();
-
- /* Following methods are meant to be overwritten by custom widgets to
- * specify it's behavior when mouse is moved around.
- * For example, containers should process these event and pass them to children
- * using their public OnMouse*() */
- virtual void _OnInnerMouseMovedOver(SCoordinates p);
- virtual void _OnInnerMouseIn();
- virtual void _OnInnerMouseOut();
-
/* These methods should calculate widget's interior's size (excluding the
* margin).
* These are the methods you should override when creating a custom widget. */
=== modified file 'src/WButton.cpp'
--- src/WButton.cpp 2013-08-04 11:05:30 +0000
+++ src/WButton.cpp 2013-08-04 16:35:32 +0000
@@ -7,11 +7,11 @@
WButton::WButton(std::string _text) : text(_text){
SetForegroundColor(al_map_rgb(120, 140, 200));
+ event_clicked.Subscribe(this,&WButton::_ClickReaction);
+ event_mouse_in.Subscribe(this,&WButton::_MouseInReaction);
+ event_mouse_out.Subscribe(this,&WButton::_MouseOutReaction);
}
-void WButton::SetClickReaction(void (*f)()){
- _click_reaction = f;
-}
void WButton::SetForegroundColor(ALLEGRO_COLOR c){
float r,g,b;
al_unmap_rgb_f(c,&r,&g,&b);
@@ -34,15 +34,14 @@
al_draw_text(CDisplay::main_font, al_map_rgb(255,255,255), w/2, h/2 - 8, ALLEGRO_ALIGN_CENTRE, text.c_str());
}
-void WButton::_OnInnerClicked(){
- if(_click_reaction == nullptr) return;
- _click_reaction();
+void WButton::_ClickReaction(SCoordinates){
+ event_activated.Happen();
}
-void WButton::_OnInnerMouseIn(){
+void WButton::_MouseInReaction(){
color = pointed_color;
SetNeedsRedrawing();
}
-void WButton::_OnInnerMouseOut(){
+void WButton::_MouseOutReaction(){
color = foreground_color;
SetNeedsRedrawing();
}
=== modified file 'src/WButton.hpp'
--- src/WButton.hpp 2013-08-04 11:05:30 +0000
+++ src/WButton.hpp 2013-08-04 16:35:32 +0000
@@ -10,20 +10,19 @@
WButton(std::string);
std::string text;
+ CEvent<> event_activated;
- void SetClickReaction(void (*f)());
void SetForegroundColor(ALLEGRO_COLOR);
private:
virtual void _Redraw(int w, int h) override;
-
- void (*_click_reaction)() = nullptr;
+
ALLEGRO_COLOR foreground_color;
ALLEGRO_COLOR pointed_color;
ALLEGRO_COLOR color;
- virtual void _OnInnerClicked() override;
- virtual void _OnInnerMouseIn() override;
- virtual void _OnInnerMouseOut() override;
+ void _ClickReaction(SCoordinates);
+ void _MouseInReaction();
+ void _MouseOutReaction();
virtual int _GetInnerMinimalHeight() override;
virtual int _GetInnerMinimalWidth () override;
=== modified file 'src/WFixed.cpp'
--- src/WFixed.cpp 2013-08-04 11:05:30 +0000
+++ src/WFixed.cpp 2013-08-04 16:35:32 +0000
@@ -2,6 +2,9 @@
#include "allegro5/allegro_primitives.h"
WFixed::WFixed(int w, int h) : _width(w), _height(h){
+ event_clicked.Subscribe(this,&WFixed::_ClickReaction);
+ event_mouse_out.Subscribe(this,&WFixed::_MouseOutReaction);
+ event_mouse_over.Subscribe(this,&WFixed::_MouseOverReaction);
}
void WFixed::_Redraw(int w, int h){
@@ -30,7 +33,7 @@
SetNeedsRedrawing();
}
-void WFixed::_OnInnerClicked(SCoordinates p){
+void WFixed::_ClickReaction(SCoordinates p){
for(auto q : items){
IDrawable* d = q.first;
SCoordinates c = q.second;
@@ -40,7 +43,7 @@
}
}
-void WFixed::_OnInnerMouseMovedOver(SCoordinates p){
+void WFixed::_MouseOverReaction(SCoordinates p){
// Find the previously pointed children
IDrawable* previous = nullptr;
for(auto q : items) if(q.first->pointed) {previous = q.first; break;}
@@ -68,7 +71,7 @@
current->OnMouseMovedOver(SCoordinates(dx,dy));
}
}
-void WFixed::_OnInnerMouseOut(){
+void WFixed::_MouseOutReaction(){
// Find the previously pointed children
IDrawable* previous = nullptr;
for(auto q : items) if(q.first->pointed) {previous = q.first; break;}
=== modified file 'src/WFixed.hpp'
--- src/WFixed.hpp 2013-08-04 11:05:30 +0000
+++ src/WFixed.hpp 2013-08-04 16:35:32 +0000
@@ -19,9 +19,9 @@
virtual void _Redraw(int w, int h);
- virtual void _OnInnerClicked(SCoordinates p);
- virtual void _OnInnerMouseMovedOver(SCoordinates p);
- virtual void _OnInnerMouseOut();
+ void _ClickReaction(SCoordinates p);
+ void _MouseOverReaction(SCoordinates p);
+ void _MouseOutReaction();
virtual int _GetInnerMinimalWidth () override;
virtual int _GetInnerMinimalHeight() override;
=== modified file 'src/WHBox.cpp'
--- src/WHBox.cpp 2013-08-04 11:05:30 +0000
+++ src/WHBox.cpp 2013-08-04 16:35:32 +0000
@@ -1,7 +1,11 @@
#include "WHBox.hpp"
#include <allegro5/allegro_primitives.h>
-WHBox::WHBox() {}
+WHBox::WHBox() {
+ event_clicked.Subscribe(this,&WHBox::_ClickReaction);
+ event_mouse_out.Subscribe(this,&WHBox::_MouseOutReaction);
+ event_mouse_over.Subscribe(this,&WHBox::_MouseOverReaction);
+}
void WHBox::_Redraw(int w, int h){
@@ -47,7 +51,7 @@
return sum;
}
-void WHBox::_OnInnerClicked(SCoordinates p){
+void WHBox::_ClickReaction(SCoordinates p){
if(_current_height < p.y || _current_width < p.x) return;
int sum=0;
for(auto i : children){
@@ -63,7 +67,7 @@
}
}
}
-void WHBox::_OnInnerMouseMovedOver(SCoordinates p){
+void WHBox::_MouseOverReaction(SCoordinates p){
// Find the previously pointed children
IDrawable* previous = nullptr;
for(auto q : children) if(q.item.pointed) {previous = &q.item; break;}
@@ -97,7 +101,7 @@
current->OnMouseMovedOver(SCoordinates(dx,dy));
}
}
-void WHBox::_OnInnerMouseOut(){
+void WHBox::_MouseOutReaction(){
// Find the previously pointed children
IDrawable* previous = nullptr;
for(auto q : children) if(q.item.pointed) {previous = &q.item; break;}
=== modified file 'src/WHBox.hpp'
--- src/WHBox.hpp 2013-08-04 11:05:30 +0000
+++ src/WHBox.hpp 2013-08-04 16:35:32 +0000
@@ -10,9 +10,9 @@
private:
virtual void _Redraw(int w, int h) override;
- virtual void _OnInnerClicked(SCoordinates p) override;
- virtual void _OnInnerMouseMovedOver(SCoordinates p) override;
- virtual void _OnInnerMouseOut() override;
+ void _ClickReaction(SCoordinates p);
+ void _MouseOverReaction(SCoordinates p);
+ void _MouseOutReaction();
virtual int _GetInnerMinimalHeight() override;
virtual int _GetInnerMinimalWidth () override;
=== modified file 'src/WLayeredView.cpp'
--- src/WLayeredView.cpp 2013-08-04 11:05:30 +0000
+++ src/WLayeredView.cpp 2013-08-04 16:35:32 +0000
@@ -1,6 +1,10 @@
#include "WLayeredView.hpp"
-WLayeredView::WLayeredView() {}
+WLayeredView::WLayeredView() {
+ event_clicked.Subscribe(this,&WLayeredView::_ClickReaction);
+ event_mouse_out.Subscribe(this,&WLayeredView::_MouseOutReaction);
+ event_mouse_over.Subscribe(this,&WLayeredView::_MouseOverReaction);
+}
void WLayeredView::ActivateLayer(IDrawable& layer)
{
@@ -41,7 +45,7 @@
i->item.Draw(SDrawArea(0,0,w,h));
}
-void WLayeredView::_OnInnerClicked(SCoordinates p)
+void WLayeredView::_ClickReaction(SCoordinates p)
{
//pass "click signal" to currently active layer
for(auto i = children.begin() ; i != children.end() ; i++)
@@ -52,7 +56,7 @@
}
}
-void WLayeredView::_OnInnerMouseMovedOver(SCoordinates p){
+void WLayeredView::_MouseOverReaction(SCoordinates p){
// Find the previously pointed children
IDrawable* previous = nullptr;
for(auto i : children) if(i.item.pointed) {previous = &i.item; break;}
@@ -75,6 +79,15 @@
}
}
+void WLayeredView::_MouseOutReaction(){
+ // Find the previously pointed children
+ IDrawable* previous = nullptr;
+ for(auto i : children) if(i.item.pointed) {previous = &i.item; break;}
+ if(previous != nullptr){
+ previous->OnMouseOut();
+ }
+}
+
int WLayeredView::_GetInnerMinimalWidth()
{
//find maximum of layers' widths
=== modified file 'src/WLayeredView.hpp'
--- src/WLayeredView.hpp 2013-08-04 11:05:30 +0000
+++ src/WLayeredView.hpp 2013-08-04 16:35:32 +0000
@@ -15,8 +15,9 @@
private:
virtual void _Redraw(int w, int h);
- virtual void _OnInnerClicked(SCoordinates p);
- virtual void _OnInnerMouseMovedOver(SCoordinates p);
+ void _ClickReaction(SCoordinates p);
+ void _MouseOverReaction(SCoordinates p);
+ void _MouseOutReaction();
virtual int _GetInnerMinimalWidth ();
virtual int _GetInnerMinimalHeight();
=== modified file 'src/WVBox.cpp'
--- src/WVBox.cpp 2013-08-04 11:05:30 +0000
+++ src/WVBox.cpp 2013-08-04 16:35:32 +0000
@@ -1,7 +1,11 @@
#include "WVBox.hpp"
#include <allegro5/allegro_primitives.h>
-WVBox::WVBox() {}
+WVBox::WVBox() {
+ event_clicked.Subscribe(this,&WVBox::_ClickReaction);
+ event_mouse_out.Subscribe(this,&WVBox::_MouseOutReaction);
+ event_mouse_over.Subscribe(this,&WVBox::_MouseOverReaction);
+}
void WVBox::_Redraw(int w, int h){
@@ -47,7 +51,7 @@
return max;
}
-void WVBox::_OnInnerClicked(SCoordinates p){
+void WVBox::_ClickReaction(SCoordinates p){
if(_current_height < p.y || _current_width < p.x) return;
int sum=0;
for(auto i : children){
@@ -64,7 +68,7 @@
}
}
-void WVBox::_OnInnerMouseMovedOver(SCoordinates p){
+void WVBox::_MouseOverReaction(SCoordinates p){
// Find the previously pointed children
IDrawable* previous = nullptr;
for(auto q : children) if(q.item.pointed) {previous = &q.item; break;}
@@ -98,7 +102,7 @@
current->OnMouseMovedOver(SCoordinates(dx,dy));
}
}
-void WVBox::_OnInnerMouseOut(){
+void WVBox::_MouseOutReaction(){
// Find the previously pointed children
IDrawable* previous = nullptr;
for(auto q : children) if(q.item.pointed) {previous = &q.item; break;}
=== modified file 'src/WVBox.hpp'
--- src/WVBox.hpp 2013-08-04 11:05:30 +0000
+++ src/WVBox.hpp 2013-08-04 16:35:32 +0000
@@ -10,9 +10,9 @@
private:
virtual void _Redraw(int w, int h) override;
- virtual void _OnInnerClicked(SCoordinates p) override;
- virtual void _OnInnerMouseMovedOver(SCoordinates p) override;
- virtual void _OnInnerMouseOut() override;
+ void _ClickReaction(SCoordinates p);
+ void _MouseOverReaction(SCoordinates p);
+ void _MouseOutReaction();
virtual int _GetInnerMinimalHeight() override;
virtual int _GetInnerMinimalWidth () override;
=== modified file 'src/main.cpp'
--- src/main.cpp 2013-08-04 09:55:33 +0000
+++ src/main.cpp 2013-08-04 16:35:32 +0000
@@ -31,30 +31,30 @@
WLabel label_main_menu("Main menu");
WButton button1("Button1");
- button1.SetClickReaction( [](){std::cout << "Button1 clicked" << std::endl;} );
+ button1.event_activated.Subscribe( [](){std::cout << "Button1 clicked" << std::endl;} );
WButton button2("Button2");
- button2.SetClickReaction( [](){std::cout << "Button2 clicked" << std::endl;} );
+ button2.event_activated.Subscribe( [](){std::cout << "Button2 clicked" << std::endl;} );
WButton button_to_2("To menu 2");
- button_to_2.SetClickReaction( [](){CDisplay::SetMainWidget(*additional_menu);} );
+ button_to_2.event_activated.Subscribe( [](){CDisplay::SetMainWidget(*additional_menu);} );
WButton button_quit("QUIT");
button_quit.SetForegroundColor( al_map_rgb(230, 100, 90) );
- button_quit.SetClickReaction( [](){main_menu->ActivateLayer(*dialog_box);} );
+ button_quit.event_activated.Subscribe( [](){main_menu->ActivateLayer(*dialog_box);} );
WLabel label_additional_menu("Menu 2");
WButton button3("Button3");
- button3.SetClickReaction( [](){std::cout << "Button3 (menu 2) clicked" << std::endl;} );
+ button3.event_activated.Subscribe( [](){std::cout << "Button3 (menu 2) clicked" << std::endl;} );
WButton button_to_main("To main menu");
- button_to_main.SetClickReaction( [](){CDisplay::SetMainWidget(*main_menu);} );
+ button_to_main.event_activated.Subscribe( [](){CDisplay::SetMainWidget(*main_menu);} );
WImage image_smile("../data/smile.png");
WLabel label_dialog_box("Are you sure?");
WButton button_yes("Yes");
- button_yes.SetClickReaction(Quit);
+ button_yes.event_activated.Subscribe(Quit);
WButton button_no("No");
- button_no.SetClickReaction( [](){main_menu->HideLayer(*dialog_box);} );
+ button_no.event_activated.Subscribe( [](){main_menu->HideLayer(*dialog_box);} );
main_menu_vbox->SetSpacing(3); // gaps between buttons
main_menu_vbox->SetMargin(5); // empty frame around the box
Follow ups