--- a/projects/openttd.vcproj Thu Mar 01 13:26:30 2007 +0000
+++ b/projects/openttd.vcproj Thu Mar 01 22:48:46 2007 +0000
@@ -1082,6 +1082,9 @@
RelativePath=".\..\src\widget\widget_stickybox.cpp">
</File>
<File
+ RelativePath=".\..\src\widget\window_event.cpp">
+ </File>
+ <File
RelativePath=".\..\src\widget\window_event_base.h">
</File>
<File
--- a/projects/openttd_vs80.vcproj Thu Mar 01 13:26:30 2007 +0000
+++ b/projects/openttd_vs80.vcproj Thu Mar 01 22:48:46 2007 +0000
@@ -1660,6 +1660,10 @@
>
</File>
<File
+ RelativePath=".\..\src\widget\window_event.cpp"
+ >
+ </File>
+ <File
RelativePath=".\..\src\widget\window_event_base.h"
>
</File>
--- a/source.list Thu Mar 01 13:26:30 2007 +0000
+++ b/source.list Thu Mar 01 22:48:46 2007 +0000
@@ -333,6 +333,7 @@
widget/widget_resizebox.cpp
widget/widget_scrollbar.cpp
widget/widget_stickybox.cpp
+widget/window_event.cpp
widget/window_event_base.h
widget/window_events.hpp
--- a/src/intro_gui.cpp Thu Mar 01 13:26:30 2007 +0000
+++ b/src/intro_gui.cpp Thu Mar 01 22:48:46 2007 +0000
@@ -164,7 +164,7 @@
gui::WidgetPtr m_button1;
WindowT()
- : BaseWindow(WC_SELECT_GAME)
+ : BaseWindow(WC_TEST1)
{
}
--- a/src/widget/widget.h Thu Mar 01 13:26:30 2007 +0000
+++ b/src/widget/widget.h Thu Mar 01 22:48:46 2007 +0000
@@ -119,6 +119,7 @@
virtual void OnKeyPress(EvtKeyPress &ev) {};
virtual void OnLeftClick(EvtClick &ev);
virtual void OnRightClick(EvtRightClick &ev);
+ virtual void OnMouseOver(EvtMouseOver &ev) {};
/**
* Dispatch event by its type calling appropriate OnCreate(), OnDestroy(), etc.
@@ -145,6 +146,14 @@
typedef ReflectHandlerDelegateT<Twnd_cls, Tevt_code> Delegate;
AddHandler(new Delegate(handler));
}
+
+ /** add capture handler using default delegate type (CaptureHandlerDelegateT<>) */
+ template <class Twd_cls, EventCode Tevt_code> CaptureTicket CaptureEventsT(Twd_cls *wd, void (Twd_cls::*handler)(EventT<Tevt_code>&))
+ {
+ CaptureTicket ticket = EventCaptureStack::AddHandler(new CaptureHandlerDelegateT<Twd_cls, Tevt_code>(wd, handler));
+ return ticket;
+ }
+
};
struct CompositeWidget : public Widget {
@@ -183,6 +192,7 @@
protected:
bool m_pushed;
+ CaptureTicket m_ticket_pressed;
public:
Button()
@@ -195,6 +205,8 @@
, m_pushed(false)
{}
+ void OnCapturePressed(EvtMouseOver &e);
+
/*virtual*/ void DrawBackground(EvtPaint &ev);
/*virtual*/ void OnLeftClick(EvtClick &ev);
--- a/src/widget/widget_button.cpp Thu Mar 01 13:26:30 2007 +0000
+++ b/src/widget/widget_button.cpp Thu Mar 01 22:48:46 2007 +0000
@@ -19,6 +19,20 @@
namespace gui {
+void Button::OnCapturePressed(EvtMouseOver &e)
+{
+ if (_left_button_down) {
+ e.SetHandled();
+ return;
+ }
+ m_pushed = false;
+ m_ticket_pressed.Release();
+
+ EvtClick ev(Point(0, 0));
+ ev.m_widget = this;
+ CallHandlers(ev);
+}
+
/*virtual*/ void Button::DrawBackground(EvtPaint &ev)
{
DrawFrameRect(m_color, m_pushed ? FR_LOWERED : FR_NONE);
@@ -26,9 +40,9 @@
/*virtual*/ void Button::OnLeftClick(EvtClick &ev)
{
- m_pushed = !m_pushed;
- CallHandlers(ev);
+ m_pushed = true;
ev.SetHandled();
+ m_ticket_pressed = CaptureEventsT(this, &Button::OnCapturePressed);
Invalidate();
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/widget/window_event.cpp Thu Mar 01 22:48:46 2007 +0000
@@ -0,0 +1,139 @@
+/* $Id$ */
+
+#include "../stdafx.h"
+#include <stdarg.h>
+#include "../openttd.h"
+#include "../debug.h"
+#include "../functions.h"
+#include "../map.h"
+#include "../player.h"
+#include "../window.h"
+#include "../gfx.h"
+#include "../viewport.h"
+#include "../console.h"
+#include "../variables.h"
+#include "../table/sprites.h"
+#include "../genworld.h"
+#include "../helpers.hpp"
+#include "window_events.hpp"
+
+namespace gui {
+
+EventCaptureStack::CaptureTicket::CaptureTicket()
+ : m_it(EventCaptureStack::GetInstance().m_handlers.end())
+{}
+
+EventCaptureStack::CaptureTicket::CaptureTicket(EventCaptureStack::Handlers::iterator it)
+: m_it(it)
+{}
+
+EventCaptureStack::CaptureTicket::CaptureTicket(const CaptureTicket &src)
+ : m_it(src.Detach())
+{}
+
+EventCaptureStack::CaptureTicket::~CaptureTicket()
+{
+ Release();
+}
+
+EventCaptureStack::CaptureTicket& EventCaptureStack::CaptureTicket::operator = (const CaptureTicket &src)
+{
+ Attach(src.Detach());
+ return *this;
+}
+
+void EventCaptureStack::CaptureTicket::Attach(EventCaptureStack::Handlers::iterator it)
+{
+ Release();
+ m_it = it;
+}
+
+EventCaptureStack::Handlers::iterator EventCaptureStack::CaptureTicket::Detach() const
+{
+ EventCaptureStack::Handlers::iterator it = m_it;
+ m_it = EventCaptureStack::GetInstance().m_handlers.end();
+ return it;
+}
+
+void EventCaptureStack::CaptureTicket::Release()
+{
+ EventCaptureStack &stack = EventCaptureStack::GetInstance();
+ EventCaptureStack::Handlers &handlers = stack.m_handlers;
+ if (m_it != handlers.end()) {
+ stack.InternalRemove(m_it);
+ m_it = handlers.end();
+ }
+}
+
+EventCaptureStack::Handlers::iterator EventCaptureStack::InternalAdd(EventHandlerDelegate *delegate)
+{
+ EventCaptureStack::Handlers::iterator it = m_handlers.insert(m_handlers.begin(), EventHandlerDelegatePtr(delegate));
+ return it;
+}
+
+void EventCaptureStack::InternalRemove(EventCaptureStack::Handlers::iterator it)
+{
+ if (it != m_handlers.end()) {
+ m_handlers.erase(it);
+ }
+}
+
+bool EventCaptureStack::InternalHandleEvent(EventBase &e)
+{
+ /** Need to think of some better way how to walk through all items in safe way.
+ * safe == "should not crash if any item is removed during handler->HandleEvent() call" */
+ if (m_handlers.empty()) return false;
+ for(Handlers::iterator it = m_handlers.begin(); it != m_handlers.end();) {
+ EventHandlerDelegatePtr &handler = (*it);
+ if (handler.IsNull() || handler->m_code != e.GetCode()) {
+ ++it;
+ continue;
+ }
+ Handlers::iterator it_temp = it;
+ it_temp = m_handlers.insert(++it_temp, EventHandlerDelegatePtr());
+ handler->HandleEvent(NULL, e);
+ it = it_temp;
+ ++it;
+ m_handlers.erase(it_temp);
+ if (e.IsHandled()) return true;
+ };
+ return false;
+}
+
+//bool EventCaptureStack::InternalHandleEvent(EventBase &e)
+//{
+// for (Handlers::reverse_iterator rit = m_handlers.rbegin(); !m_handlers.empty() && rit != m_handlers.rend();) {
+// EventHandlerDelegatePtr handler = (*rit);
+// ++rit;
+// if (handler->m_code == e.GetCode()) {
+// handler->HandleEvent(NULL, e);
+// if (e.IsHandled()) return true;
+// }
+// }
+// return false;
+//}
+//
+/*static*/ EventCaptureStack& EventCaptureStack::GetInstance()
+{
+ static EventCaptureStack stack;
+ return stack;
+}
+
+/*static*/ bool EventCaptureStack::HandleEvent(EventBase &e)
+{
+ EventCaptureStack &stack = GetInstance();
+ bool ret = stack.InternalHandleEvent(e);
+ return ret;
+}
+
+/*static*/ EventCaptureStack::CaptureTicket EventCaptureStack::AddHandler(EventHandlerDelegate *delegate)
+{
+ EventCaptureStack &stack = GetInstance();
+ EventCaptureStack::Handlers::iterator it = stack.InternalAdd(delegate);
+ EventCaptureStack::CaptureTicket ticket(it);
+ return ticket;
+}
+
+
+}; // namespace gui
+
--- a/src/widget/window_event_base.h Thu Mar 01 13:26:30 2007 +0000
+++ b/src/widget/window_event_base.h Thu Mar 01 22:48:46 2007 +0000
@@ -171,12 +171,99 @@
virtual void HandleEvent(BaseWindow *w, EventBase &e)
{
assert(e.GetCode() == Tevt_code);
+ assert(w != NULL);
Twnd_cls &wnd = *(Twnd_cls*)w;
EventT<Tevt_code> &ev = *(EventT<Tevt_code>*)&e;
(wnd.*m_handler)(ev);
}
};
+/**
+ * Delegate for capture handlers (inside widget)
+ */
+template <class Twd_cls, EventCode Tevt_code> struct CaptureHandlerDelegateT : public EventHandlerDelegate {
+ Twd_cls *m_widget;
+ void (Twd_cls::*m_handler)(EventT<Tevt_code>&);
+
+ CaptureHandlerDelegateT(Twd_cls *wd, void (Twd_cls::*handler)(EventT<Tevt_code>&))
+ : EventHandlerDelegate(Tevt_code)
+ , m_widget(wd)
+ , m_handler(handler)
+ {
+ assert(wd != NULL);
+ }
+
+ virtual void HandleEvent(BaseWindow *w, EventBase &e)
+ {
+ assert(e.GetCode() == Tevt_code);
+ EventT<Tevt_code> &ev = *(EventT<Tevt_code>*)&e;
+ (m_widget->*m_handler)(ev);
+ }
+};
+
+
+/**
+ * Event Capture Stack - widgets can register their Event Handler Delegates here
+ * to override default event processing dynamically. For example when moving window
+ * by dragging its caption bar, CaptionBar widget can capture mouse move events and
+ * process them until the mouse button is released.
+ * When gui event occurs, the stack is searched from front (last added delegate) to
+ * back and if the appropriate handler delegate (same event code) is found, it is
+ * called (given the event object). If this handler wants to prevent further event
+ * processing (by other handlers) it must set EventBase::m_handled to true by calling
+ * e->SetHandled();. Otherwise the next handler is taken from the stack or the event
+ * is processed by standard way.
+ */
+struct EventCaptureStack {
+ /** delegates are stored as a list of smart pointers to delegates */
+ typedef std::list<AdaptT<EventHandlerDelegatePtr> > Handlers;
+
+ /**
+ * Capture Ticket - issued by EventCaptureStack when new handler registers itself
+ * in order to capture input events. Widget must save it in the safe place to keep
+ * the input capture. When ticket gets destroyed, handler is automatically removed
+ * from the capture stack.
+ */
+ struct CaptureTicket {
+ protected:
+ mutable Handlers::iterator m_it; ///< iterator to that stack (to know which handler it belongs to)
+ public:
+ CaptureTicket(); ///< constructs clean ticket
+ CaptureTicket(Handlers::iterator it); ///< constructs ticket from iterator
+ CaptureTicket(const CaptureTicket &src); ///< takeover iterator (detach from src, attach to this)
+ ~CaptureTicket(); ///< call Release() and destroy ticket
+ void Attach(Handlers::iterator it); ///< Attach iterator to the ticket
+ Handlers::iterator Detach() const; ///< Detach iterator from ticket (give up ownership)
+ void Release(); ///< remove Event Capture Delegate from the Stack
+ CaptureTicket& operator = (const CaptureTicket &src);
+ };
+
+protected:
+
+ Handlers m_handlers; ///< handler delegates are stored here
+
+ /** add new delegate into list and return its iterator - called from Add() */
+ Handlers::iterator InternalAdd(EventHandlerDelegate *delegate);
+ /** remove handler referenced by the given iterator - called from CaptureTicket::Release() */
+ void InternalRemove(Handlers::iterator it);
+ /** handle event and return true if event was captured */
+ bool InternalHandleEvent(EventBase &e);
+ /** return the instance of EventCaptureStack - assuming there is only one global instance */
+ static EventCaptureStack& GetInstance();
+
+public:
+ /**
+ * iterate through capture handler list and try to handle event
+ * @return true if event was handled (no default processing is necessary)
+ */
+ static bool HandleEvent(EventBase &e);
+
+ /** use it to add your own capture handler */
+ static CaptureTicket AddHandler(EventHandlerDelegate *delegate);
+};
+
+typedef EventCaptureStack::CaptureTicket CaptureTicket;
+
}; // namespace gui
#endif /* WINDOW_EVENT_BASE_H */
--- a/src/widget/window_events.hpp Thu Mar 01 13:26:30 2007 +0000
+++ b/src/widget/window_events.hpp Thu Mar 01 22:48:46 2007 +0000
@@ -43,6 +43,14 @@
{}
};
+template <> struct EventT<EVT_MOUSEOVER> : public EventBaseT<EVT_MOUSEOVER, &Widget::OnMouseOver> {
+ Point m_pt;
+
+ EventT(const PointRaw &pt)
+ : m_pt(pt)
+ {}
+};
+
--- a/src/window.cpp Thu Mar 01 13:26:30 2007 +0000
+++ b/src/window.cpp Thu Mar 01 22:48:46 2007 +0000
@@ -1497,6 +1497,13 @@
static bool HandleMouseOver(void)
{
+ Point ptGlobal(_cursor.pos.x, _cursor.pos.y);
+ /* try if the event is being captured */
+ gui::EvtMouseOver evt_capture(ptGlobal);
+ if (gui::EventCaptureStack::HandleEvent(evt_capture)) {
+ return true;
+ }
+
static WindowClass last_cls;
static WindowNumber last_num;