# HG changeset patch # User darkvater # Date 2005-04-05 21:03:30 # Node ID 720d51a7e71e8a10bc0b5d60c0b3dac0a2a4fcbb # Parent ab216ce5579e583d573ba5d256780693dff01f9e (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. diff --git a/network_gui.c b/network_gui.c --- a/network_gui.c +++ b/network_gui.c @@ -1362,13 +1362,16 @@ void ShowJoinStatusWindowAfterJoin(void) 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 @@ press_ok:; } } 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); diff --git a/news_gui.c b/news_gui.c --- a/news_gui.c +++ b/news_gui.c @@ -98,6 +98,11 @@ void DrawNewsBorder(const Window *w) 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 @@ static void NewsWindowProc(Window *w, Wi } 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 @@ static void ShowNewspaper(NewsItem *ni) if (sound != 0) SndPlayFx(sound); - top = _screen.height - 4; + top = _screen.height; switch (ni->display_mode) { case NM_NORMAL: case NM_CALLBACK: { diff --git a/window.c b/window.c --- a/window.c +++ b/window.c @@ -289,6 +289,18 @@ Window *BringWindowToFrontById(WindowCla 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 @@ Window *BringWindowToFront(Window *w) 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 @@ Window *BringWindowToFront(Window *w) 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 @@ bool IsWindowOfPrototype(Window *w, cons 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 @@ void AssignWidgetToWindow(Window *w, con 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 @@ Window *AllocateWindow( 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;icustom);i++) - w->custom[i] = 0; - } - _last_window++; SetWindowDirty(w); @@ -1225,17 +1247,13 @@ static Window *MaybeBringWindowToFront(W { 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 @@ static Window *MaybeBringWindowToFront(W 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; diff --git a/window.h b/window.h --- a/window.h +++ b/window.h @@ -61,6 +61,10 @@ typedef struct Widget { 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 @@ union WindowEvent { 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 @@ typedef struct { uint step_height; } ResizeInfo; +typedef struct { + int msg; + int wparam; + int lparam; +} Message; + struct Window { uint16 flags4; WindowClass window_class; @@ -280,6 +297,7 @@ struct Window { //const WindowDesc *desc; uint32 desc_flags; + Message message; byte custom[WINDOW_CUSTOM_SIZE]; }; @@ -425,6 +443,13 @@ typedef struct vehiclelist_d { } 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 @@ enum WindowEvents { WE_MOUSEOVER = 20, WE_ON_EDIT_TEXT_CANCEL = 21, WE_RESIZE = 22, + WE_MESSAGE = 23 }; @@ -519,6 +545,8 @@ void DrawOverlappedWindow(Window *w, int 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);