New netplay UI.

git-svn-id: svn://svn.code.sf.net/p/sc2/code/trunk@2500 8092fc87-c524-0410-9efc-e669fe64eaf9
This commit is contained in:
mcmartin
2006-11-13 11:49:41 +00:00
parent 84590a8b36
commit b105684fbd
4 changed files with 579 additions and 46 deletions
+552 -4
View File
@@ -20,32 +20,575 @@
#include "cnctdlg.h"
#include "controls.h"
#include "colors.h"
#include "setup.h"
#include "libs/graphics/widgets.h"
#include "netplay/netoptions.h"
#define MCD_WIDTH 260
#define MCD_HEIGHT 100
#define MENU_FRAME_RATE (ONE_SECOND / 20)
typedef struct connect_dialog_state
{
BOOLEAN (*InputFunc) (struct connect_dialog_state *pInputState);
COUNT MenuRepeatDelay;
DWORD NextTime;
BOOLEAN Initialized;
int which_side;
int option;
int confirmed;
} CONNECT_DIALOG_STATE;
static void DrawConnectDialog (void);
typedef CONNECT_DIALOG_STATE *PCONNECT_DIALOG_STATE;
static WIDGET_MENU_SCREEN menu;
static WIDGET_BUTTON buttons[3];
static WIDGET_SLIDER slider;
static WIDGET_TEXTENTRY texts[2];
static WIDGET *menu_widgets[] = {
(WIDGET *)&buttons[1],
(WIDGET *)&texts[0],
(WIDGET *)&buttons[0],
(WIDGET *)&slider,
(WIDGET *)&texts[1],
(WIDGET *)&buttons[2] };
static BOOLEAN done;
/* This kind of sucks, but the Button callbacks need access to the
* CONNECT_DIALOG_STATE, so we need a pointer to it */
static PCONNECT_DIALOG_STATE current_state;
static int do_connect (WIDGET *self, int event);
static int do_listen (WIDGET *self, int event);
static int do_cancel (WIDGET *self, int event);
static void
MCD_DrawMenuScreen (WIDGET *_self, int x, int y)
{
int widget_index, widget_y;
WIDGET_MENU_SCREEN *self = (WIDGET_MENU_SCREEN *)_self;
widget_y = y + 8;
for (widget_index = 0; widget_index < self->num_children; widget_index++)
{
WIDGET *c = self->child[widget_index];
(*c->draw)(c, x, widget_y);
widget_y += (*c->height)(c) + 8;
}
}
static void
MCD_DrawButton (WIDGET *_self, int x, int y)
{
WIDGET_BUTTON *self = (WIDGET_BUTTON *)_self;
COLOR oldtext;
COLOR inactive, selected;
FONT oldfont = SetContextFont (StarConFont);
FRAME oldFontEffect = SetContextFontEffect (NULL);
TEXT t;
selected = MENU_HIGHLIGHT_COLOR;
inactive = MENU_TEXT_COLOR;
t.baseline.x = 160;
t.baseline.y = y;
t.align = ALIGN_CENTER;
t.CharCount = ~0;
t.pStr = self->name;
if (widget_focus == _self)
{
oldtext = SetContextForeGroundColor (selected);
}
else
{
oldtext = SetContextForeGroundColor (inactive);
}
font_DrawText (&t);
SetContextFontEffect (oldFontEffect);
SetContextFont (oldfont);
SetContextForeGroundColor (oldtext);
(void) x;
}
static void
MCD_DrawSlider (WIDGET *_self, int x, int y)
{
WIDGET_SLIDER *self = (WIDGET_SLIDER *)_self;
COLOR oldtext;
COLOR default_color, selected;
FONT oldfont = SetContextFont (StarConFont);
FRAME oldFontEffect = SetContextFontEffect (NULL);
TEXT t;
RECT r;
int tick = (MCD_WIDTH) / 8;
default_color = MENU_TEXT_COLOR;
selected = MENU_HIGHLIGHT_COLOR;
t.baseline.x = x;
t.baseline.y = y;
t.align = ALIGN_LEFT;
t.CharCount = ~0;
t.pStr = self->category;
if (widget_focus == _self)
{
oldtext = SetContextForeGroundColor (selected);
}
else
{
oldtext = SetContextForeGroundColor (default_color);
}
font_DrawText (&t);
r.corner.x = t.baseline.x + 3 * tick;
r.corner.y = t.baseline.y - 4;
r.extent.height = 2;
r.extent.width = 3 * tick;
DrawFilledRectangle (&r);
r.extent.width = 3;
r.extent.height = 8;
r.corner.y = t.baseline.y - 7;
r.corner.x = t.baseline.x + 3 * tick + (3 * tick * (self->value - self->min) /
(self->max - self->min)) - 1;
DrawFilledRectangle (&r);
(*self->draw_value)(self, t.baseline.x + 7 * tick, t.baseline.y);
SetContextFontEffect (oldFontEffect);
SetContextFont (oldfont);
SetContextForeGroundColor (oldtext);
}
void
MCD_DrawTextEntry (WIDGET *_self, int x, int y)
{
WIDGET_TEXTENTRY *self = (WIDGET_TEXTENTRY *)_self;
COLOR oldtext;
COLOR inactive, default_color, selected;
FONT oldfont = SetContextFont (StarConFont);
FRAME oldFontEffect = SetContextFontEffect (NULL);
TEXT t;
default_color = MENU_TEXT_COLOR;
selected = MENU_HIGHLIGHT_COLOR;
inactive = MENU_TEXT_COLOR;
BatchGraphics ();
t.baseline.x = x;
t.baseline.y = y;
t.align = ALIGN_LEFT;
t.CharCount = ~0;
t.pStr = self->category;
if (widget_focus == _self)
{
oldtext = SetContextForeGroundColor (selected);
}
else
{
oldtext = SetContextForeGroundColor (default_color);
}
font_DrawText (&t);
/* Force string termination */
self->value[WIDGET_TEXTENTRY_WIDTH-1] = 0;
t.baseline.y = y;
t.CharCount = utf8StringCount (self->value);
t.pStr = self->value;
if (!(self->state & WTE_EDITING))
{ // normal or selected state
t.baseline.x = 160;
t.align = ALIGN_CENTER;
if (widget_focus == _self)
{
oldtext = SetContextForeGroundColor (selected);
}
else
{
oldtext = SetContextForeGroundColor (inactive);
}
font_DrawText (&t);
}
else
{ // editing state
COUNT i;
RECT text_r;
BYTE char_deltas[WIDGET_TEXTENTRY_WIDTH];
PBYTE pchar_deltas;
RECT r;
SIZE leading;
t.baseline.x = x + 90;
t.align = ALIGN_LEFT;
// calc background box dimensions
// XXX: this may need some tuning, especially if a
// different font is used. The font 'leading' values
// are not what they should be.
#define BOX_VERT_OFFSET 2
GetContextFontLeading (&leading);
r.corner.x = t.baseline.x - 1;
r.corner.y = t.baseline.y - leading + BOX_VERT_OFFSET;
r.extent.width = MCD_WIDTH - r.corner.x - 10;
r.extent.height = leading + 2;
TextRect (&t, &text_r, char_deltas);
#if 0
// XXX: this should potentially be used in ChangeCallback
if ((text_r.extent.width + 2) >= r.extent.width)
{ // the text does not fit the input box size and so
// will not fit when displayed later
UnbatchGraphics ();
// disallow the change
return (FALSE);
}
#endif
oldtext = SetContextForeGroundColor (selected);
DrawFilledRectangle (&r);
// calculate the cursor position and draw it
pchar_deltas = char_deltas;
for (i = self->cursor_pos; i > 0; --i)
r.corner.x += (SIZE)*pchar_deltas++;
if (self->cursor_pos < t.CharCount) /* cursor mid-line */
--r.corner.x;
if (self->state & WTE_BLOCKCUR)
{ // Use block cursor for keyboardless systems
if (self->cursor_pos == t.CharCount)
{ // cursor at end-line -- use insertion point
r.extent.width = 1;
}
else if (self->cursor_pos + 1 == t.CharCount)
{ // extra pixel for last char margin
r.extent.width = (SIZE)*pchar_deltas + 2;
}
else
{ // normal mid-line char
r.extent.width = (SIZE)*pchar_deltas + 1;
}
}
else
{ // Insertion point cursor
r.extent.width = 1;
}
// position cursor within input field rect
++r.corner.x;
++r.corner.y;
r.extent.height -= 2;
SetContextForeGroundColor (MENU_TEXT_COLOR);
DrawFilledRectangle (&r);
SetContextForeGroundColor (inactive);
font_DrawText (&t);
}
UnbatchGraphics ();
SetContextFontEffect (oldFontEffect);
SetContextFont (oldfont);
SetContextForeGroundColor (oldtext);
}
/* Text entry stuff, mostly C&Ped from setupmenu.c. Could use some
* refactoring, as redraw_menu () is the only real change. */
static BOOLEAN
OnTextEntryChange (PTEXTENTRY_STATE pTES)
{
WIDGET_TEXTENTRY *widget = (WIDGET_TEXTENTRY *) pTES->CbParam;
widget->cursor_pos = pTES->CursorPos;
if (pTES->JoystickMode)
widget->state |= WTE_BLOCKCUR;
else
widget->state &= ~WTE_BLOCKCUR;
// XXX TODO: Here, we can examine the text entered so far
// to make sure it fits on the screen, for example,
// and return FALSE to disallow the last change
return TRUE; // allow change
}
static BOOLEAN
OnTextEntryFrame (PTEXTENTRY_STATE pTES)
{
WIDGET_TEXTENTRY *widget = (WIDGET_TEXTENTRY *) pTES->CbParam;
DrawConnectDialog ();
SleepThreadUntil (pTES->NextTime);
pTES->NextTime = GetTimeCounter () + MENU_FRAME_RATE;
return TRUE; // continue
}
static int
OnTextEntryEvent (WIDGET_TEXTENTRY *widget)
{ // Going to edit the text
TEXTENTRY_STATE tes;
UNICODE revert_buf[256];
// position cursor at the end of text
widget->cursor_pos = utf8StringCount (widget->value);
widget->state = WTE_EDITING;
DrawConnectDialog ();
// make a backup copy for revert on cancel
utf8StringCopy (revert_buf, sizeof (revert_buf), widget->value);
// text entry setup
tes.Initialized = FALSE;
tes.MenuRepeatDelay = 0;
tes.NextTime = GetTimeCounter () + MENU_FRAME_RATE;
tes.BaseStr = widget->value;
tes.MaxSize = widget->maxlen;
tes.CursorPos = widget->cursor_pos;
tes.CbParam = widget;
tes.ChangeCallback = OnTextEntryChange;
tes.FrameCallback = OnTextEntryFrame;
// SetMenuSounds (0, MENU_SOUND_SELECT);
if (!DoTextEntry (&tes))
{ // editing failed (canceled) -- revert the changes
utf8StringCopy (widget->value, widget->maxlen, revert_buf);
}
else
{
if (widget->onChange)
{
(*(widget->onChange))(widget);
}
}
widget->state = WTE_NORMAL;
DrawConnectDialog ();
return TRUE; // event handled
}
/* Button response routines */
static int
do_connect (WIDGET *self, int event)
{
if (event == WIDGET_EVENT_SELECT)
{
/* These assignments are safe exactly because texts[] is file-scope) */
netplayOptions.peer[current_state->which_side].host = texts[0].value;
netplayOptions.peer[current_state->which_side].port = texts[1].value;
netplayOptions.peer[current_state->which_side].isServer = FALSE;
current_state->confirmed = TRUE;
netplayOptions.inputDelay = slider.value;
done = TRUE;
}
(void)self;
return FALSE;
}
static int
do_listen (WIDGET *self, int event)
{
if (event == WIDGET_EVENT_SELECT)
{
/* These assignments are safe exactly because texts[] is file-scope) */
netplayOptions.peer[current_state->which_side].port = texts[1].value;
netplayOptions.peer[current_state->which_side].isServer = TRUE;
netplayOptions.inputDelay = slider.value;
current_state->confirmed = TRUE;
done = TRUE;
}
(void)self;
return FALSE;
}
static int
do_cancel (WIDGET *self, int event)
{
if (event == WIDGET_EVENT_SELECT)
{
current_state->confirmed = FALSE;
done = TRUE;
}
(void)self;
return FALSE;
}
static void
CreateWidgets (void)
{
int i;
done = false;
for (i = 0; i < 3; i++)
{
buttons[i].tag = WIDGET_TYPE_BUTTON;
buttons[i].parent = NULL;
buttons[i].receiveFocus = Widget_ReceiveFocusSimple;
buttons[i].draw = MCD_DrawButton;
buttons[i].height = Widget_HeightOneLine;
buttons[i].width = Widget_WidthFullScreen;
}
buttons[0].name = "Connect to remote host";
buttons[1].name = "Wait for incoming connection";
buttons[2].name = "Cancel";
buttons[0].handleEvent = do_connect;
buttons[1].handleEvent = do_listen;
buttons[2].handleEvent = do_cancel;
menu.tag = WIDGET_TYPE_MENU_SCREEN;
menu.parent = NULL;
menu.receiveFocus = Widget_ReceiveFocusMenuScreen;
menu.draw = MCD_DrawMenuScreen;
menu.height = Widget_HeightFullScreen;
menu.width = Widget_WidthFullScreen;
menu.num_children = 6;
menu.child = menu_widgets;
menu.handleEvent = Widget_HandleEventMenuScreen;
slider.tag = WIDGET_TYPE_SLIDER;
slider.parent = NULL;
slider.handleEvent = Widget_HandleEventSlider;
slider.receiveFocus = Widget_ReceiveFocusSimple;
slider.draw = MCD_DrawSlider;
slider.height = Widget_HeightOneLine;
slider.width = Widget_WidthFullScreen;
slider.draw_value = Widget_Slider_DrawValue;
slider.min = 0;
slider.max = 9;
slider.step = 1;
slider.value = netplayOptions.inputDelay;
slider.category = "Net Delay";
for (i = 0; i < 2; i++)
{
texts[i].tag = WIDGET_TYPE_TEXTENTRY;
texts[i].parent = NULL;
texts[i].handleEvent = Widget_HandleEventTextEntry;
texts[i].receiveFocus = Widget_ReceiveFocusSimple;
texts[i].draw = MCD_DrawTextEntry;
texts[i].height = Widget_HeightOneLine;
texts[i].width = Widget_WidthFullScreen;
texts[i].handleEventSelect = OnTextEntryEvent;
texts[i].maxlen = WIDGET_TEXTENTRY_WIDTH-1;
texts[i].state = WTE_NORMAL;
texts[i].cursor_pos = 0;
}
texts[0].category = "Host";
texts[1].category = "Port";
/* We sometimes assign to these internals; cannot strncpy over self! */
if (texts[0].value != netplayOptions.peer[current_state->which_side].host)
{
strncpy (texts[0].value, netplayOptions.peer[current_state->which_side].host, texts[0].maxlen);
}
if (texts[1].value != netplayOptions.peer[current_state->which_side].port)
{
strncpy (texts[1].value, netplayOptions.peer[current_state->which_side].port, texts[1].maxlen);
}
texts[0].value[texts[0].maxlen]=0;
texts[1].value[texts[1].maxlen]=0;
menu.receiveFocus ((WIDGET *)&menu, WIDGET_EVENT_DOWN);
}
static void
DrawConnectDialog (void)
{
RECT r;
r.extent.width = MCD_WIDTH;
r.extent.height = MCD_HEIGHT;
r.corner.x = (SCREEN_WIDTH - r.extent.width) >> 1;
r.corner.y = (SCREEN_HEIGHT - r.extent.height) >> 1;
LockMutex (GraphicsLock);
DrawShadowedBox (&r, SHADOWBOX_BACKGROUND_COLOR,
SHADOWBOX_DARK_COLOR, SHADOWBOX_MEDIUM_COLOR);
menu.draw ((WIDGET *)&menu, r.corner.x + 10, r.corner.y + 10);
UnlockMutex (GraphicsLock);
}
static BOOLEAN
DoMeleeConnectDialog (PCONNECT_DIALOG_STATE state)
{
BOOLEAN changed;
if (!state->Initialized)
{
state->Initialized = TRUE;
SetDefaultMenuRepeatDelay ();
state->NextTime = GetTimeCounter ();
/* Prepare widgets, draw stuff, etc. */
CreateWidgets ();
DrawConnectDialog ();
}
/* But this is skeletal, so just end immediately */
return FALSE;
changed = TRUE;
if (PulsedInputState.menu[KEY_MENU_UP])
{
Widget_Event (WIDGET_EVENT_UP);
}
else if (PulsedInputState.menu[KEY_MENU_DOWN])
{
Widget_Event (WIDGET_EVENT_DOWN);
}
else if (PulsedInputState.menu[KEY_MENU_LEFT])
{
Widget_Event (WIDGET_EVENT_LEFT);
}
else if (PulsedInputState.menu[KEY_MENU_RIGHT])
{
Widget_Event (WIDGET_EVENT_RIGHT);
}
else if (PulsedInputState.menu[KEY_MENU_SELECT])
{
Widget_Event (WIDGET_EVENT_SELECT);
}
else if (PulsedInputState.menu[KEY_MENU_CANCEL])
{
Widget_Event (WIDGET_EVENT_CANCEL);
}
else if (PulsedInputState.menu[KEY_MENU_DELETE])
{
Widget_Event (WIDGET_EVENT_DELETE);
}
else
{
changed = FALSE;
}
if (changed)
{
DrawConnectDialog ();
}
SleepThreadUntil (state->NextTime + MENU_FRAME_RATE);
state->NextTime = GetTimeCounter ();
return !((GLOBAL (CurrentActivity) & CHECK_ABORT) ||
done);
}
BOOLEAN
@@ -56,10 +599,15 @@ MeleeConnectDialog (int side)
state.Initialized = FALSE;
state.which_side = side;
state.InputFunc = DoMeleeConnectDialog;
state.confirmed = TRUE;
current_state = &state;
DoInput ((PVOID)&state, TRUE);
return TRUE;
current_state = NULL;
return state.confirmed;
}
#endif /* NETPLAY */
+4 -1
View File
@@ -43,7 +43,7 @@ typedef enum {
NUM_WIDGET_TYPES
} WIDGET_TYPE;
#define WIDGET_TEXTENTRY_WIDTH 30
#define WIDGET_TEXTENTRY_WIDTH 50
#define WIDGET_CONTROLENTRY_WIDTH 16
typedef struct _widget {
@@ -213,4 +213,7 @@ void Widget_DrawControlEntry (WIDGET *_self, int x, int y);
void Widget_Slider_DrawValue (WIDGET_SLIDER *self, int x, int y);
/* Other implementations will need these values */
extern WIDGET *widget_focus;
#endif /* _WIDGETS_H */