(svn r8958) [cpp_gui] -Add: Widget can now capture specified gui events cpp_gui
authorKUDr
Thu, 01 Mar 2007 22:48:46 +0000
branchcpp_gui
changeset 6271 0ad100a98853
parent 6270 5b2d0642fb81
child 6272 43637acd46b5
(svn r8958) [cpp_gui] -Add: Widget can now capture specified gui events
-Codechange: Button now releases itself and generates EvtClick event when mouse button is released
projects/openttd.vcproj
projects/openttd_vs80.vcproj
source.list
src/intro_gui.cpp
src/widget/widget.h
src/widget/widget_button.cpp
src/widget/window_event.cpp
src/widget/window_event_base.h
src/widget/window_events.hpp
src/window.cpp
--- 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;