(svn r2152) - Fix: Chatbar in MP games is now on-top of the news window.
authordarkvater
Tue, 05 Apr 2005 21:03:30 +0000
changeset 1648 747061dca705
parent 1647 85783475f6d0
child 1649 09c7a5d6e65a
(svn r2152) - Fix: Chatbar in MP games is now on-top of the news window.
- CodeChange: Introduction of SendWindowMessage() where a window can send another window a message (ala windows style msg, wparam, lparam). Messages can be sent by windowclass and by windowpointer.
- CodeChange: IsVitalWindow() simplifies a lot of checks for window handling that need to know what windows it can close, or be on top of, etc.
network_gui.c
news_gui.c
window.c
window.h
--- a/network_gui.c	Tue Apr 05 20:52:55 2005 +0000
+++ b/network_gui.c	Tue Apr 05 21:03:30 2005 +0000
@@ -1362,13 +1362,16 @@
 static void ChatWindowWndProc(Window *w, WindowEvent *e)
 {
 	static bool closed = false;
-	switch(e->event) {
-	case WE_PAINT: {
+	switch (e->event) {
+	case WE_CREATE:
+		SendWindowMessage(WC_NEWS_WINDOW, 0, WE_CREATE, w->height, 0);
+		closed = false;
+		break;
 
+	case WE_PAINT:
 		DrawWindowWidgets(w);
-
 		DrawEditBox(w, 1);
-	} break;
+		break;
 
 	case WE_CLICK:
 		switch(e->click.widget) {
@@ -1418,11 +1421,8 @@
 		}
 	} break;
 
-	case WE_CREATE:
-		closed = false;
-		break;
-
 	case WE_DESTROY:
+		SendWindowMessage(WC_NEWS_WINDOW, 0, WE_DESTROY, 0, 0);
 		// If the window is not closed yet, it means it still needs to send a CANCEL
 		if (!closed) {
 			Window *parent = FindWindowById(WP(w,querystr_d).wnd_class, WP(w,querystr_d).wnd_num);
--- a/news_gui.c	Tue Apr 05 20:52:55 2005 +0000
+++ b/news_gui.c	Tue Apr 05 21:03:30 2005 +0000
@@ -98,6 +98,11 @@
 static void NewsWindowProc(Window *w, WindowEvent *e)
 {
 	switch (e->event) {
+	case WE_CREATE: { /* If chatbar is open at creation time, we need to go above it */
+		const Window *w1 = FindWindowById(WC_SEND_NETWORK_MSG, 0);
+		w->message.msg = (w1 != NULL) ? w1->height : 0;
+	} break;
+
 	case WE_PAINT: {
 		const NewsItem *ni = WP(w, news_d).ni;
 		ViewPort *vp;
@@ -184,17 +189,26 @@
 		}
 		break;
 
-	case WE_TICK: {
-		int y = max(w->top - 4, _screen.height - w->height);
-		if (y == w->top)
-			return;
+	case WE_MESSAGE: /* The chatbar has notified us that is was either created or closed */
+		switch (e->message.msg) {
+			case WE_CREATE: w->message.msg = e->message.wparam; break;
+			case WE_DESTROY: w->message.msg = 0; break;
+			break;
+		}
+		break;
+
+	case WE_TICK: { /* Scroll up newsmessages from the bottom in steps of 4 pixels */
+		int diff;
+		int y = max(w->top - 4, _screen.height - w->height - 12 - w->message.msg);
+		if (y == w->top) return;
 
 		if (w->viewport != NULL)
 			w->viewport->top += y - w->top;
 
+		diff = abs(w->top - y);
 		w->top = y;
 
-		SetDirtyBlocks(w->left, w->top, w->left + w->width, w->top + w->height + 4);
+		SetDirtyBlocks(w->left, w->top - diff, w->left + w->width, w->top + w->height);
 	} break;
 	}
 }
@@ -339,7 +353,7 @@
 	if (sound != 0)
 		SndPlayFx(sound);
 
-	top = _screen.height - 4;
+	top = _screen.height;
 	switch (ni->display_mode) {
 		case NM_NORMAL:
 		case NM_CALLBACK: {
--- a/window.c	Tue Apr 05 20:52:55 2005 +0000
+++ b/window.c	Tue Apr 05 21:03:30 2005 +0000
@@ -289,6 +289,18 @@
 	return w;
 }
 
+static inline bool IsVitalWindow(const Window *w)
+{
+	WindowClass wc = w->window_class;
+	return (wc == WC_MAIN_TOOLBAR || wc == WC_STATUS_BAR || wc == WC_NEWS_WINDOW || wc == WC_SEND_NETWORK_MSG);
+}
+
+/** On clicking on a window, make it the frontmost window of all. However
+ * there are certain windows that always need to be on-top; these include
+ * - Toolbar, Statusbar (always on)
+ * - New window, Chatbar (only if open)
+ * @param w window that is put into the foreground
+ */
 Window *BringWindowToFront(Window *w)
 {
 	Window *v;
@@ -298,7 +310,7 @@
 	do {
 		if (--v < _windows)
 			return w;
-	} while (v->window_class == WC_MAIN_TOOLBAR || v->window_class == WC_STATUS_BAR || v->window_class == WC_NEWS_WINDOW);
+	} while (IsVitalWindow(v));
 
 	if (w == v)
 		return w;
@@ -314,30 +326,36 @@
 	return v;
 }
 
-/* We have run out of windows, so find a suitable candidate for replacement.
- * Keep all important windows intact */
+/** We have run out of windows, so find a suitable candidate for replacement.
+ * Keep all important windows intact. These are
+ * - Main window (gamefield), Toolbar, Statusbar (always on)
+ * - News window, Chatbar (when on)
+ * - Any sticked windows since we wanted to keep these
+ * @return w pointer to the window that is going to be deleted
+ */
 static Window *FindDeletableWindow(void)
 {
 	Window *w;
 	for (w = _windows; w < endof(_windows); w++) {
-		if (w->window_class != WC_MAIN_WINDOW && w->window_class != WC_MAIN_TOOLBAR &&
-			  w->window_class != WC_STATUS_BAR && w->window_class != WC_NEWS_WINDOW &&
-				!(w->flags4 & WF_STICKY) )
+		if (w->window_class != WC_MAIN_WINDOW && !IsVitalWindow(w) && !(w->flags4 & WF_STICKY) )
 				return w;
 	}
 	return NULL;
 }
 
-/* A window must be freed, and all are marked as important windows. Ease the
- * restriction a bit by allowing to delete sticky windows */
+/** A window must be freed, and all are marked as important windows. Ease the
+ * restriction a bit by allowing to delete sticky windows. Keep important/vital
+ * windows intact (Main window, Toolbar, Statusbar, News Window, Chatbar)
+ * @see FindDeletableWindow()
+ * @return w Pointer to the window that is being deleted
+ */
 static Window *ForceFindDeletableWindow(void)
 {
 	Window *w;
 	for (w = _windows;; w++) {
 		assert(w < _last_window);
 
-		if (w->window_class != WC_MAIN_WINDOW && w->window_class != WC_MAIN_TOOLBAR &&
-			  w->window_class != WC_STATUS_BAR && w->window_class != WC_NEWS_WINDOW)
+		if (w->window_class != WC_MAIN_WINDOW && !IsVitalWindow(w))
 				return w;
 	}
 }
@@ -347,7 +365,7 @@
 	return (w->original_widget == widget);
 }
 
-/* Copies 'widget' to 'w->widget' */
+/* Copies 'widget' to 'w->widget' to allow for resizable windows */
 void AssignWidgetToWindow(Window *w, const Widget *widget)
 {
 	w->original_widget = widget;
@@ -366,19 +384,26 @@
 		w->widget = NULL;
 }
 
+/** Open a new window. If there is no space for a new window, close an open
+ * window. Try to avoid stickied windows, but if there is no else, close one of
+ * those as well. Then make sure all created windows are below some always-on-top
+ * ones. Finally set all variables and call the WE_CREATE event
+ * @param x offset in pixels from the left of the screen
+ * @param y offset in pixels from the top of the screen
+ * @param width width in pixels of the window
+ * @param height height in pixels of the window
+ * @param *proc @see WindowProc function to call when any messages/updates happen to the window
+ * @param cls @see WindowClass class of the window, used for identification and grouping
+ * @param *widget @see Widget pointer to the window layout and various elements
+ * @return @see Window pointer of the newly created window
+ */
 Window *AllocateWindow(
-							int x,
-							int y,
-							int width,
-							int height,
-							WindowProc *proc,
-							WindowClass cls,
-							const Widget *widget)
+							int x, int y, int width, int height,
+							WindowProc *proc, WindowClass cls, const Widget *widget)
 {
-	Window *w;
+	Window *w = _last_window; // last window keeps track of the highest open window
 
-	w = _last_window;
-
+	// We have run out of windows, close one and use that as the place for our new one
 	if (w >= endof(_windows)) {
 		w = FindDeletableWindow();
 
@@ -389,56 +414,53 @@
 		w = _last_window;
 	}
 
-	if (w != _windows && cls != WC_NEWS_WINDOW) {
+	/* XXX - This very strange construction makes sure that the chatbar is always
+	 * on top of other windows. Why? It is created as last_window (so, on top).
+	 * Any other window will go below toolbar/statusbar/news window, which implicitely
+	 * also means it is below the chatbar. Very likely needs heavy improvement
+	 * to de-braindeadize */
+	if (w != _windows && cls != WC_SEND_NETWORK_MSG) {
 		Window *v;
 
+		/* XXX - if not this order (toolbar/statusbar and then news), game would
+		 * crash because it will try to copy a negative size for the news-window.
+		 * Eg. window was already moved BELOW news (which is below toolbar/statusbar)
+		 * and now needs to move below those too. That is a negative move. */
 		v = FindWindowById(WC_MAIN_TOOLBAR, 0);
-		if (v) {
+		if (v != NULL) {
 			memmove(v+1, v, (byte*)w - (byte*)v);
 			w = v;
 		}
 
 		v = FindWindowById(WC_STATUS_BAR, 0);
-		if (v) {
+		if (v != NULL) {
+			memmove(v+1, v, (byte*)w - (byte*)v);
+			w = v;
+		}
+
+		v = FindWindowById(WC_NEWS_WINDOW, 0);
+		if (v != NULL) {
 			memmove(v+1, v, (byte*)w - (byte*)v);
 			w = v;
 		}
 	}
 
-	/* XXX: some more code here */
+	// Set up window properties
+	memset(w, 0, sizeof(Window));
 	w->window_class = cls;
-	w->flags4 = WF_WHITE_BORDER_MASK;
+	w->flags4 = WF_WHITE_BORDER_MASK; // just opened windows have a white border
 	w->caption_color = 0xFF;
-	w->window_number = 0;
 	w->left = x;
 	w->top = y;
 	w->width = width;
 	w->height = height;
-	w->viewport = NULL;
-	w->desc_flags = 0;
-//	w->landscape_assoc = 0xFFFF;
 	w->wndproc = proc;
-	w->click_state = 0;
-	w->disabled_state = 0;
-	w->hidden_state = 0;
-//	w->unk22 = 0xFFFF;
-	w->vscroll.pos = 0;
-	w->vscroll.count = 0;
-	w->hscroll.pos = 0;
-	w->hscroll.count = 0;
-	w->widget = NULL;
 	AssignWidgetToWindow(w, widget);
 	w->resize.width = width;
 	w->resize.height = height;
 	w->resize.step_width = 1;
 	w->resize.step_height = 1;
 
-	{
-		uint i;
-		for (i=0;i<lengthof(w->custom);i++)
-			w->custom[i] = 0;
-	}
-
 	_last_window++;
 
 	SetWindowDirty(w);
@@ -1225,17 +1247,13 @@
 {
 	Window *u;
 
-	if (w->window_class == WC_MAIN_WINDOW ||
-			w->window_class == WC_MAIN_TOOLBAR ||
-			w->window_class == WC_STATUS_BAR ||
-			w->window_class == WC_NEWS_WINDOW ||
-			w->window_class == WC_TOOLTIPS ||
-			w->window_class == WC_DROPDOWN_MENU)
+	if (w->window_class == WC_MAIN_WINDOW || IsVitalWindow(w) ||
+			w->window_class == WC_TOOLTIPS    || w->window_class == WC_DROPDOWN_MENU)
 				return w;
 
-	for(u=w; ++u != _last_window;) {
-		if (u->window_class == WC_MAIN_WINDOW || u->window_class==WC_MAIN_TOOLBAR || u->window_class==WC_STATUS_BAR ||
-				u->window_class == WC_NEWS_WINDOW || u->window_class == WC_TOOLTIPS || u->window_class == WC_DROPDOWN_MENU)
+	for (u = w; ++u != _last_window;) {
+		if (u->window_class == WC_MAIN_WINDOW || IsVitalWindow(u) ||
+			  u->window_class == WC_TOOLTIPS    || u->window_class == WC_DROPDOWN_MENU)
 				continue;
 
 		if (w->left + w->width <= u->left ||
@@ -1250,6 +1268,37 @@
 	return w;
 }
 
+/** Send a message from one window to another. The receiving window is found by
+ * @param w @see Window pointer pointing to the other window
+ * @param msg Specifies the message to be sent
+ * @param wparam Specifies additional message-specific information
+ * @param lparam Specifies additional message-specific information
+ */
+void SendWindowMessageW(Window *w, uint msg, uint wparam, uint lparam)
+{
+	WindowEvent e;
+
+	e.message.event  = WE_MESSAGE;
+	e.message.msg    = msg;
+	e.message.wparam = wparam;
+	e.message.lparam = lparam;
+
+	w->wndproc(w, &e);
+}
+
+/** Send a message from one window to another. The receiving window is found by
+ * @param wnd_class @see WindowClass class AND
+ * @param wnd_num @see WindowNumber number, mostly 0
+ * @param msg Specifies the message to be sent
+ * @param wparam Specifies additional message-specific information
+ * @param lparam Specifies additional message-specific information
+ */
+void SendWindowMessage(WindowClass wnd_class, WindowNumber wnd_num, uint msg, uint wparam, uint lparam)
+{
+	Window *w = FindWindowById(wnd_class, wnd_num);
+	if (w != NULL) SendWindowMessageW(w, msg, wparam, lparam);
+}
+
 static void HandleKeypress(uint32 key)
 {
 	Window *w;
--- a/window.h	Tue Apr 05 20:52:55 2005 +0000
+++ b/window.h	Tue Apr 05 21:03:30 2005 +0000
@@ -61,6 +61,10 @@
 	uint16 tooltips;
 } Widget;
 
+/* XXX - outside "byte event" so you can set event directly without going into
+ * the union elements at first. Because of this every first element of the union
+ * MUST BE 'byte event'. Whoever did this must get shot! Scheduled for immediate
+ * rewrite after 0.4.0 */
 union WindowEvent {
 	byte event;
 	struct {
@@ -117,6 +121,13 @@
 		byte ascii;  // 8-bit ASCII-value of the key
 		uint16 keycode;// untranslated key (including shift-state)
 	} keypress;
+
+	struct {
+		byte event;
+		uint msg;    // message to be sent
+		uint wparam; // additional message-specific information
+		uint lparam; // additional message-specific information
+	} message;
 };
 
 enum WindowKeyCodes {
@@ -259,6 +270,12 @@
 	uint step_height;
 } ResizeInfo;
 
+typedef struct {
+		int msg;
+		int wparam;
+		int lparam;
+} Message;
+
 struct Window {
 	uint16 flags4;
 	WindowClass window_class;
@@ -280,6 +297,7 @@
 	//const WindowDesc *desc;
 	uint32 desc_flags;
 
+	Message message;
 	byte custom[WINDOW_CUSTOM_SIZE];
 };
 
@@ -425,6 +443,13 @@
 } vehiclelist_d;
 assert_compile(WINDOW_CUSTOM_SIZE >= sizeof(vehiclelist_d));
 
+typedef struct message_d {
+	int msg;
+	int wparam;
+	int lparam;
+} message_d;
+assert_compile(WINDOW_CUSTOM_SIZE >= sizeof(message_d));
+
 enum WindowEvents {
 	WE_CLICK = 0,
 	WE_PAINT = 1,
@@ -449,6 +474,7 @@
 	WE_MOUSEOVER = 20,
 	WE_ON_EDIT_TEXT_CANCEL = 21,
 	WE_RESIZE = 22,
+	WE_MESSAGE = 23
 };
 
 
@@ -519,6 +545,8 @@
 void CallWindowEventNP(Window *w, int event);
 void CallWindowTickEvent(void);
 void SetWindowDirty(Window *w);
+void SendWindowMessageW(Window *w, uint msg, uint wparam, uint lparam);
+void SendWindowMessage(WindowClass wnd_class, WindowNumber wnd_num, uint msg, uint wparam, uint lparam);
 
 Window *FindWindowById(WindowClass cls, WindowNumber number);
 void DeleteWindow(Window *w);