1
0
Fork 0

(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.
release/0.4.5
darkvater 2005-04-05 21:03:30 +00:00
parent 4aa7cf8e9f
commit 22d36985cd
4 changed files with 159 additions and 68 deletions

View File

@ -1362,13 +1362,16 @@ void ShowJoinStatusWindowAfterJoin(void)
static void ChatWindowWndProc(Window *w, WindowEvent *e) static void ChatWindowWndProc(Window *w, WindowEvent *e)
{ {
static bool closed = false; static bool closed = false;
switch(e->event) { switch (e->event) {
case WE_PAINT: { case WE_CREATE:
SendWindowMessage(WC_NEWS_WINDOW, 0, WE_CREATE, w->height, 0);
closed = false;
break;
case WE_PAINT:
DrawWindowWidgets(w); DrawWindowWidgets(w);
DrawEditBox(w, 1); DrawEditBox(w, 1);
} break; break;
case WE_CLICK: case WE_CLICK:
switch(e->click.widget) { switch(e->click.widget) {
@ -1418,11 +1421,8 @@ press_ok:;
} }
} break; } break;
case WE_CREATE:
closed = false;
break;
case WE_DESTROY: 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 the window is not closed yet, it means it still needs to send a CANCEL
if (!closed) { if (!closed) {
Window *parent = FindWindowById(WP(w,querystr_d).wnd_class, WP(w,querystr_d).wnd_num); Window *parent = FindWindowById(WP(w,querystr_d).wnd_class, WP(w,querystr_d).wnd_num);

View File

@ -98,6 +98,11 @@ void DrawNewsBorder(const Window *w)
static void NewsWindowProc(Window *w, WindowEvent *e) static void NewsWindowProc(Window *w, WindowEvent *e)
{ {
switch (e->event) { 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: { case WE_PAINT: {
const NewsItem *ni = WP(w, news_d).ni; const NewsItem *ni = WP(w, news_d).ni;
ViewPort *vp; ViewPort *vp;
@ -184,17 +189,26 @@ static void NewsWindowProc(Window *w, WindowEvent *e)
} }
break; break;
case WE_TICK: { case WE_MESSAGE: /* The chatbar has notified us that is was either created or closed */
int y = max(w->top - 4, _screen.height - w->height); switch (e->message.msg) {
if (y == w->top) case WE_CREATE: w->message.msg = e->message.wparam; break;
return; 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) if (w->viewport != NULL)
w->viewport->top += y - w->top; w->viewport->top += y - w->top;
diff = abs(w->top - y);
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; } break;
} }
} }
@ -339,7 +353,7 @@ static void ShowNewspaper(NewsItem *ni)
if (sound != 0) if (sound != 0)
SndPlayFx(sound); SndPlayFx(sound);
top = _screen.height - 4; top = _screen.height;
switch (ni->display_mode) { switch (ni->display_mode) {
case NM_NORMAL: case NM_NORMAL:
case NM_CALLBACK: { case NM_CALLBACK: {

157
window.c
View File

@ -289,6 +289,18 @@ Window *BringWindowToFrontById(WindowClass cls, WindowNumber number)
return w; 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 *BringWindowToFront(Window *w)
{ {
Window *v; Window *v;
@ -298,7 +310,7 @@ Window *BringWindowToFront(Window *w)
do { do {
if (--v < _windows) if (--v < _windows)
return w; 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) if (w == v)
return w; return w;
@ -314,30 +326,36 @@ Window *BringWindowToFront(Window *w)
return v; return v;
} }
/* We have run out of windows, so find a suitable candidate for replacement. /** We have run out of windows, so find a suitable candidate for replacement.
* Keep all important windows intact */ * 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) static Window *FindDeletableWindow(void)
{ {
Window *w; Window *w;
for (w = _windows; w < endof(_windows); w++) { for (w = _windows; w < endof(_windows); w++) {
if (w->window_class != WC_MAIN_WINDOW && w->window_class != WC_MAIN_TOOLBAR && if (w->window_class != WC_MAIN_WINDOW && !IsVitalWindow(w) && !(w->flags4 & WF_STICKY) )
w->window_class != WC_STATUS_BAR && w->window_class != WC_NEWS_WINDOW &&
!(w->flags4 & WF_STICKY) )
return w; return w;
} }
return NULL; return NULL;
} }
/* A window must be freed, and all are marked as important windows. Ease the /** A window must be freed, and all are marked as important windows. Ease the
* restriction a bit by allowing to delete sticky windows */ * 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) static Window *ForceFindDeletableWindow(void)
{ {
Window *w; Window *w;
for (w = _windows;; w++) { for (w = _windows;; w++) {
assert(w < _last_window); assert(w < _last_window);
if (w->window_class != WC_MAIN_WINDOW && w->window_class != WC_MAIN_TOOLBAR && if (w->window_class != WC_MAIN_WINDOW && !IsVitalWindow(w))
w->window_class != WC_STATUS_BAR && w->window_class != WC_NEWS_WINDOW)
return w; return w;
} }
} }
@ -347,7 +365,7 @@ bool IsWindowOfPrototype(Window *w, const Widget *widget)
return (w->original_widget == widget); 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) void AssignWidgetToWindow(Window *w, const Widget *widget)
{ {
w->original_widget = widget; w->original_widget = widget;
@ -366,19 +384,26 @@ void AssignWidgetToWindow(Window *w, const Widget *widget)
w->widget = NULL; 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( Window *AllocateWindow(
int x, int x, int y, int width, int height,
int y, WindowProc *proc, WindowClass cls, const Widget *widget)
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)) { if (w >= endof(_windows)) {
w = FindDeletableWindow(); w = FindDeletableWindow();
@ -389,56 +414,53 @@ Window *AllocateWindow(
w = _last_window; 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; 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); v = FindWindowById(WC_MAIN_TOOLBAR, 0);
if (v) { if (v != NULL) {
memmove(v+1, v, (byte*)w - (byte*)v); memmove(v+1, v, (byte*)w - (byte*)v);
w = v; w = v;
} }
v = FindWindowById(WC_STATUS_BAR, 0); 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); memmove(v+1, v, (byte*)w - (byte*)v);
w = v; w = v;
} }
} }
/* XXX: some more code here */ // Set up window properties
memset(w, 0, sizeof(Window));
w->window_class = cls; 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->caption_color = 0xFF;
w->window_number = 0;
w->left = x; w->left = x;
w->top = y; w->top = y;
w->width = width; w->width = width;
w->height = height; w->height = height;
w->viewport = NULL;
w->desc_flags = 0;
// w->landscape_assoc = 0xFFFF;
w->wndproc = proc; 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); AssignWidgetToWindow(w, widget);
w->resize.width = width; w->resize.width = width;
w->resize.height = height; w->resize.height = height;
w->resize.step_width = 1; w->resize.step_width = 1;
w->resize.step_height = 1; w->resize.step_height = 1;
{
uint i;
for (i=0;i<lengthof(w->custom);i++)
w->custom[i] = 0;
}
_last_window++; _last_window++;
SetWindowDirty(w); SetWindowDirty(w);
@ -1225,17 +1247,13 @@ static Window *MaybeBringWindowToFront(Window *w)
{ {
Window *u; Window *u;
if (w->window_class == WC_MAIN_WINDOW || if (w->window_class == WC_MAIN_WINDOW || IsVitalWindow(w) ||
w->window_class == WC_MAIN_TOOLBAR || w->window_class == WC_TOOLTIPS || w->window_class == WC_DROPDOWN_MENU)
w->window_class == WC_STATUS_BAR ||
w->window_class == WC_NEWS_WINDOW ||
w->window_class == WC_TOOLTIPS ||
w->window_class == WC_DROPDOWN_MENU)
return w; return w;
for(u=w; ++u != _last_window;) { 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 || if (u->window_class == WC_MAIN_WINDOW || IsVitalWindow(u) ||
u->window_class == WC_NEWS_WINDOW || u->window_class == WC_TOOLTIPS || u->window_class == WC_DROPDOWN_MENU) u->window_class == WC_TOOLTIPS || u->window_class == WC_DROPDOWN_MENU)
continue; continue;
if (w->left + w->width <= u->left || if (w->left + w->width <= u->left ||
@ -1250,6 +1268,37 @@ static Window *MaybeBringWindowToFront(Window *w)
return 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) static void HandleKeypress(uint32 key)
{ {
Window *w; Window *w;

View File

@ -61,6 +61,10 @@ typedef struct Widget {
uint16 tooltips; uint16 tooltips;
} Widget; } 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 { union WindowEvent {
byte event; byte event;
struct { struct {
@ -117,6 +121,13 @@ union WindowEvent {
byte ascii; // 8-bit ASCII-value of the key byte ascii; // 8-bit ASCII-value of the key
uint16 keycode;// untranslated key (including shift-state) uint16 keycode;// untranslated key (including shift-state)
} keypress; } 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 { enum WindowKeyCodes {
@ -259,6 +270,12 @@ typedef struct {
uint step_height; uint step_height;
} ResizeInfo; } ResizeInfo;
typedef struct {
int msg;
int wparam;
int lparam;
} Message;
struct Window { struct Window {
uint16 flags4; uint16 flags4;
WindowClass window_class; WindowClass window_class;
@ -280,6 +297,7 @@ struct Window {
//const WindowDesc *desc; //const WindowDesc *desc;
uint32 desc_flags; uint32 desc_flags;
Message message;
byte custom[WINDOW_CUSTOM_SIZE]; byte custom[WINDOW_CUSTOM_SIZE];
}; };
@ -425,6 +443,13 @@ typedef struct vehiclelist_d {
} vehiclelist_d; } vehiclelist_d;
assert_compile(WINDOW_CUSTOM_SIZE >= sizeof(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 { enum WindowEvents {
WE_CLICK = 0, WE_CLICK = 0,
WE_PAINT = 1, WE_PAINT = 1,
@ -449,6 +474,7 @@ enum WindowEvents {
WE_MOUSEOVER = 20, WE_MOUSEOVER = 20,
WE_ON_EDIT_TEXT_CANCEL = 21, WE_ON_EDIT_TEXT_CANCEL = 21,
WE_RESIZE = 22, WE_RESIZE = 22,
WE_MESSAGE = 23
}; };
@ -519,6 +545,8 @@ void DrawOverlappedWindow(Window *w, int left, int top, int right, int bottom);
void CallWindowEventNP(Window *w, int event); void CallWindowEventNP(Window *w, int event);
void CallWindowTickEvent(void); void CallWindowTickEvent(void);
void SetWindowDirty(Window *w); 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); Window *FindWindowById(WindowClass cls, WindowNumber number);
void DeleteWindow(Window *w); void DeleteWindow(Window *w);