Merge from trunk.

Subversion-branch: /branches/raven-branch
Subversion-revision: 1442
This commit is contained in:
Simon Howard 2009-02-08 17:52:08 +00:00
commit 67fbcdce28
9 changed files with 670 additions and 5 deletions

View file

@ -19,6 +19,7 @@ libtextscreen_a_SOURCES = \
txt_button.c txt_button.h \
txt_label.c txt_label.h \
txt_radiobutton.c txt_radiobutton.h \
txt_scrollpane.c txt_scrollpane.h \
txt_separator.c txt_separator.h \
txt_spinctrl.c txt_spinctrl.h \
txt_sdl.c txt_sdl.h \

View file

@ -82,6 +82,7 @@ void SetupWindow(void)
TXT_AddWidget(window, toplabel);
TXT_SetWidgetAlign(toplabel, TXT_HORIZ_CENTER);
//TXT_AddWidget(window, TXT_NewScrollPane(15, 4, table));
TXT_AddWidget(window, table);
for (i=0; i<5; ++i)
@ -119,7 +120,7 @@ void SetupWindow(void)
}
UpdateLabel(NULL, NULL);
TXT_AddWidget(window, TXT_NewButton2("Close Window", CloseWindow, NULL));
pwn = TXT_NewWindowAction(KEY_F1, "PWN!");
@ -133,8 +134,9 @@ void Window2(void)
{
txt_window_t *window;
txt_table_t *table;
txt_scrollpane_t *scrollpane;
int i;
window = TXT_NewWindow("Another test");
TXT_SetWindowPosition(window,
TXT_HORIZ_RIGHT,
@ -150,11 +152,56 @@ void Window2(void)
table = TXT_NewTable(2);
TXT_AddWidget(window, table);
TXT_AddWidget(table, TXT_NewLabel("String: "));
TXT_AddWidget(table, TXT_NewInputBox(&textbox_value, 30));
TXT_AddWidget(table, TXT_NewInputBox(&textbox_value, 20));
TXT_AddWidget(table, TXT_NewLabel("Int: "));
TXT_AddWidget(table, TXT_NewIntInputBox(&numbox_value, 10));
TXT_AddWidget(table, TXT_NewLabel("Spin control:"));
TXT_AddWidget(table, TXT_NewSpinControl(&numbox_value, 0, 15));
TXT_AddWidget(window, TXT_NewSeparator("Scroll pane test"));
scrollpane = TXT_NewScrollPane(40, 5, TXT_NewLabel(
"This is a scrollable pane. The contents\n"
"of this box are larger than the box\n"
"itself, but it can be scrolled around\n"
"to explore the full contents.\n"
"\n"
"Scrollable panes can be scrolled both\n"
"vertically and horizontally. They\n"
"can contain any widget. The scroll bars\n"
"appear automatically as needed.\n"
"\n"
"This is a very long line of text that forces a horizontal scrollbar"
));
TXT_AddWidget(window, scrollpane);
}
void ScrollingMenu(void)
{
txt_window_t *window;
txt_button_t *button;
txt_table_t *table;
window = TXT_NewWindow("Scrollable menu");
table = TXT_NewTable(1);
TXT_AddWidgets(table,
TXT_NewButton("Configure display"),
TXT_NewButton("Configure joystick"),
TXT_NewButton("Configure keyboard"),
TXT_NewButton("Configure mouse"),
TXT_NewButton("Configure sound"),
TXT_NewStrut(0, 1),
button = TXT_NewButton("Save Parameters and launch DOOM"),
TXT_NewStrut(0, 1),
TXT_NewButton("Start a network game"),
TXT_NewButton("Join a network game"),
TXT_NewButton("Multiplayer configuration"),
NULL);
TXT_SignalConnect(button, "pressed", PwnBox, NULL);
TXT_AddWidget(window, TXT_NewScrollPane(0, 6, table));
}
int main(int argc, char *argv[])
@ -167,6 +214,7 @@ int main(int argc, char *argv[])
TXT_SetDesktopTitle("Not Chocolate Doom Setup");
ScrollingMenu();
Window2();
SetupWindow();

View file

@ -32,6 +32,7 @@
#include "txt_inputbox.h"
#include "txt_label.h"
#include "txt_radiobutton.h"
#include "txt_scrollpane.h"
#include "txt_separator.h"
#include "txt_spinctrl.h"
#include "txt_strut.h"

View file

@ -252,6 +252,101 @@ void TXT_DrawString(char *s)
TXT_GotoXY(x + strlen(s), y);
}
void TXT_DrawHorizScrollbar(int x, int y, int w, int cursor, int range)
{
int x1;
int cursor_x;
if (!VALID_Y(y))
{
return;
}
TXT_FGColor(TXT_COLOR_BLACK);
TXT_BGColor(TXT_COLOR_GREY, 0);
TXT_GotoXY(x, y);
TXT_PutChar('\x1b');
cursor_x = x + 1;
if (range > 1)
{
cursor_x += (cursor * (w - 3)) / (range - 1);
}
if (cursor_x > x + w - 2)
{
cursor_x = x + w - 2;
}
for (x1=x+1; x1<x+w-1; ++x1)
{
if (VALID_X(x1))
{
if (x1 == cursor_x)
{
TXT_PutChar('\xdb');
}
else
{
TXT_PutChar('\xb1');
}
}
}
TXT_PutChar('\x1a');
}
void TXT_DrawVertScrollbar(int x, int y, int h, int cursor, int range)
{
int y1;
int cursor_y;
if (!VALID_X(x))
{
return;
}
TXT_FGColor(TXT_COLOR_BLACK);
TXT_BGColor(TXT_COLOR_GREY, 0);
TXT_GotoXY(x, y);
TXT_PutChar('\x18');
cursor_y = y + 1;
if (cursor_y > y + h - 2)
{
cursor_y = y + h - 2;
}
if (range > 1)
{
cursor_y += (cursor * (h - 3)) / (range - 1);
}
for (y1=y+1; y1<y+h-1; ++y1)
{
if (VALID_Y(y1))
{
TXT_GotoXY(x, y1);
if (y1 == cursor_y)
{
TXT_PutChar('\xdb');
}
else
{
TXT_PutChar('\xb1');
}
}
}
TXT_GotoXY(x, y + h - 1);
TXT_PutChar('\x19');
}
void TXT_InitClipArea(void)
{
if (cliparea == NULL)
@ -283,13 +378,17 @@ void TXT_PushClipArea(int x1, int x2, int y1, int y2)
newarea->x1 = x1;
if (x2 < newarea->x2)
newarea->x2 = x2;
if (y1 > newarea->x1)
if (y1 > newarea->y1)
newarea->y1 = y1;
if (y2 < newarea->y2)
newarea->y2 = y2;
#if 0
printf("New scrollable area: %i,%i-%i,%i\n", x1, y1, x2, y2);
#endif
// Hook into the list
newarea->next = cliparea;
cliparea = newarea;
}

View file

@ -32,6 +32,9 @@ void TXT_DrawWindowFrame(char *title, int x, int y, int w, int h);
void TXT_DrawSeparator(int x, int y, int w);
void TXT_DrawString(char *s);
void TXT_DrawHorizScrollbar(int x, int y, int w, int cursor, int range);
void TXT_DrawVertScrollbar(int x, int y, int h, int cursor, int range);
void TXT_InitClipArea(void);
void TXT_PushClipArea(int x1, int x2, int y1, int y2);
void TXT_PopClipArea(void);

442
textscreen/txt_scrollpane.c Normal file
View file

@ -0,0 +1,442 @@
// Emacs style mode select -*- C++ -*-
//-----------------------------------------------------------------------------
//
// Copyright(C) 2009 Simon Howard
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
// 02111-1307, USA.
//
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include "txt_scrollpane.h"
#include "txt_gui.h"
#include "txt_io.h"
#include "txt_main.h"
#include "txt_table.h"
#include "doomkeys.h"
#define SCROLLBAR_VERTICAL (1 << 0)
#define SCROLLBAR_HORIZONTAL (1 << 1)
static int FullWidth(txt_scrollpane_t *scrollpane)
{
if (scrollpane->child != NULL)
{
return scrollpane->child->w;
}
else
{
return 0;
}
}
static int FullHeight(txt_scrollpane_t *scrollpane)
{
if (scrollpane->child != NULL)
{
return scrollpane->child->h;
}
else
{
return 0;
}
}
// Calculate which scroll bars the pane needs.
static int NeedsScrollbars(txt_scrollpane_t *scrollpane)
{
int result;
result = 0;
if (FullWidth(scrollpane) > scrollpane->w)
{
result |= SCROLLBAR_HORIZONTAL;
}
if (FullHeight(scrollpane) > scrollpane->h)
{
result |= SCROLLBAR_VERTICAL;
}
return result;
}
// If a scrollbar isn't needed, the scroll position is reset.
static void SanityCheckScrollbars(txt_scrollpane_t *scrollpane)
{
int scrollbars;
int max_x, max_y;
scrollbars = NeedsScrollbars(scrollpane);
if ((scrollbars & SCROLLBAR_HORIZONTAL) == 0)
{
scrollpane->x = 0;
}
if ((scrollbars & SCROLLBAR_VERTICAL) == 0)
{
scrollpane->y = 0;
}
max_x = FullWidth(scrollpane) - scrollpane->w;
max_y = FullHeight(scrollpane) - scrollpane->h;
if (scrollpane->x < 0)
{
scrollpane->x = 0;
}
else if (scrollpane->x > max_x)
{
scrollpane->x = max_x;
}
if (scrollpane->y < 0)
{
scrollpane->y = 0;
}
else if (scrollpane->y > max_y)
{
scrollpane->y = max_y;
}
}
static void TXT_ScrollPaneSizeCalc(TXT_UNCAST_ARG(scrollpane))
{
TXT_CAST_ARG(txt_scrollpane_t, scrollpane);
int scrollbars;
if (scrollpane->child != NULL)
{
TXT_CalcWidgetSize(scrollpane->child);
}
// Expand as necessary (to ensure that no scrollbars are needed)?
if (scrollpane->expand_w)
{
scrollpane->w = FullWidth(scrollpane);
}
if (scrollpane->expand_h)
{
scrollpane->h = FullWidth(scrollpane);
}
scrollpane->widget.w = scrollpane->w;
scrollpane->widget.h = scrollpane->h;
// If we have scroll bars, we need to expand slightly to
// accomodate them. Eg. if we have a vertical scrollbar, we
// need to be an extra character wide.
scrollbars = NeedsScrollbars(scrollpane);
if (scrollbars & SCROLLBAR_HORIZONTAL)
{
++scrollpane->widget.h;
}
if (scrollbars & SCROLLBAR_VERTICAL)
{
++scrollpane->widget.w;
}
}
static void TXT_ScrollPaneDrawer(TXT_UNCAST_ARG(scrollpane), int selected)
{
TXT_CAST_ARG(txt_scrollpane_t, scrollpane);
int x1, y1, x2, y2;
int scrollbars;
// We set a clipping area of the scroll pane.
x1 = scrollpane->widget.x,
y1 = scrollpane->widget.y,
x2 = x1 + scrollpane->w,
y2 = y1 + scrollpane->h;
scrollbars = NeedsScrollbars(scrollpane);
if (scrollbars & SCROLLBAR_HORIZONTAL)
{
TXT_DrawHorizScrollbar(x1,
y1 + scrollpane->h,
scrollpane->w,
scrollpane->x,
FullWidth(scrollpane) - scrollpane->w);
}
if (scrollbars & SCROLLBAR_VERTICAL)
{
TXT_DrawVertScrollbar(x1 + scrollpane->w,
y1,
scrollpane->h,
scrollpane->y,
FullHeight(scrollpane) - scrollpane->h);
}
TXT_PushClipArea(x1, x2, y1, y2);
// Draw the child widget
if (scrollpane->child != NULL)
{
TXT_DrawWidget(scrollpane->child, selected);
}
// Restore old clipping area.
TXT_PopClipArea();
}
static void TXT_ScrollPaneDestructor(TXT_UNCAST_ARG(scrollpane))
{
TXT_CAST_ARG(txt_scrollpane_t, scrollpane);
if (scrollpane->child != NULL)
{
TXT_DestroyWidget(scrollpane->child);
}
}
// Hack for tables - when browsing a table inside a scroll pane,
// automatically scroll the window to show the newly-selected
// item.
static void ShowSelectedWidget(txt_scrollpane_t *scrollpane)
{
txt_widget_t *selected;
selected = TXT_GetSelectedWidget(scrollpane->child);
// Scroll up or down?
if (selected->y <= scrollpane->widget.y)
{
scrollpane->y -= scrollpane->widget.y - selected->y;
}
else if (selected->y + selected->h >
scrollpane->widget.y + scrollpane->h)
{
scrollpane->y += (selected->y + selected->h)
- (scrollpane->widget.y + scrollpane->h);
}
// Scroll left or right?
if (selected->x <= scrollpane->widget.x)
{
scrollpane->x -= scrollpane->widget.x - selected->x;
}
else if (selected->x + selected->w >
scrollpane->widget.x + scrollpane->w)
{
scrollpane->x += (selected->x + selected->w)
- (scrollpane->widget.x + scrollpane->w);
}
}
// Interpret arrow key presses as scroll commands
static int InterpretScrollKey(txt_scrollpane_t *scrollpane, int key)
{
switch (key)
{
case KEY_UPARROW:
if (scrollpane->y > 0)
{
--scrollpane->y;
return 1;
}
break;
case KEY_DOWNARROW:
if (scrollpane->y < FullHeight(scrollpane) - scrollpane->h)
{
++scrollpane->y;
return 1;
}
break;
case KEY_LEFTARROW:
if (scrollpane->x > 0)
{
--scrollpane->x;
return 1;
}
break;
case KEY_RIGHTARROW:
if (scrollpane->x < FullWidth(scrollpane) - scrollpane->w)
{
++scrollpane->x;
return 1;
}
break;
default:
break;
}
return 0;
}
static int TXT_ScrollPaneKeyPress(TXT_UNCAST_ARG(scrollpane), int key)
{
TXT_CAST_ARG(txt_scrollpane_t, scrollpane);
int result;
result = 0;
if (scrollpane->child != NULL)
{
result = TXT_WidgetKeyPress(scrollpane->child, key);
// Gross hack - if we're scrolling in a menu with the keyboard,
// automatically move the scroll pane to show the new
// selected item.
if (scrollpane->child->widget_class == &txt_table_class
&& (key == KEY_UPARROW || key == KEY_DOWNARROW
|| key == KEY_LEFTARROW || key == KEY_RIGHTARROW))
{
ShowSelectedWidget(scrollpane);
}
// If the child widget didn't use the keypress, we can see
// if it can be interpreted as a scrolling command.
if (result == 0)
{
result = InterpretScrollKey(scrollpane, key);
}
}
return result;
}
static void TXT_ScrollPaneMousePress(TXT_UNCAST_ARG(scrollpane),
int x, int y, int b)
{
TXT_CAST_ARG(txt_scrollpane_t, scrollpane);
int scrollbars;
int rel_x, rel_y;
scrollbars = NeedsScrollbars(scrollpane);
rel_x = x - scrollpane->widget.x;
rel_y = y - scrollpane->widget.y;
// Click on the horizontal scrollbar?
if ((scrollbars & SCROLLBAR_HORIZONTAL) && rel_y == scrollpane->h)
{
if (rel_x == 0)
{
--scrollpane->x;
}
else if (rel_x == scrollpane->w - 1)
{
++scrollpane->x;
}
else
{
int range = FullWidth(scrollpane) - scrollpane->w;
scrollpane->x = ((rel_x - 1) * range) / (scrollpane->w - 3);
}
return;
}
// Click on the horizontal scrollbar?
if ((scrollbars & SCROLLBAR_VERTICAL) && rel_x == scrollpane->w)
{
if (rel_y == 0)
{
--scrollpane->y;
}
else if (rel_y == scrollpane->h - 1)
{
++scrollpane->y;
}
else
{
int range = FullHeight(scrollpane) - scrollpane->h;
scrollpane->y = ((rel_y - 1) * range) / (scrollpane->h - 3);
}
return;
}
if (scrollpane->child != NULL)
{
TXT_WidgetMousePress(scrollpane->child, x, y, b);
}
}
static void TXT_ScrollPaneLayout(TXT_UNCAST_ARG(scrollpane))
{
TXT_CAST_ARG(txt_scrollpane_t, scrollpane);
SanityCheckScrollbars(scrollpane);
// The child widget takes the same position as the scroll pane
// itself, but is offset by the scroll position.
if (scrollpane->child != NULL)
{
scrollpane->child->x = scrollpane->widget.x - scrollpane->x;
scrollpane->child->y = scrollpane->widget.y - scrollpane->y;
TXT_LayoutWidget(scrollpane->child);
}
}
txt_widget_class_t txt_scrollpane_class =
{
TXT_ScrollPaneSizeCalc,
TXT_ScrollPaneDrawer,
TXT_ScrollPaneKeyPress,
TXT_ScrollPaneDestructor,
TXT_ScrollPaneMousePress,
TXT_ScrollPaneLayout,
};
txt_scrollpane_t *TXT_NewScrollPane(int w, int h, TXT_UNCAST_ARG(target))
{
TXT_CAST_ARG(txt_widget_t, target);
txt_scrollpane_t *scrollpane;
scrollpane = malloc(sizeof(txt_scrollpane_t));
TXT_InitWidget(scrollpane, &txt_scrollpane_class);
scrollpane->w = w;
scrollpane->h = h;
scrollpane->x = 0;
scrollpane->y = 0;
scrollpane->child = target;
scrollpane->expand_w = w <= 0;
scrollpane->expand_h = h <= 0;
return scrollpane;
}

View file

@ -0,0 +1,42 @@
// Emacs style mode select -*- C++ -*-
//-----------------------------------------------------------------------------
//
// Copyright(C) 2009 Simon Howard
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
// 02111-1307, USA.
//
#ifndef TXT_SCROLLPANE_H
#define TXT_SCROLLPANE_H
typedef struct txt_scrollpane_s txt_scrollpane_t;
#include "txt_widget.h"
struct txt_scrollpane_s
{
txt_widget_t widget;
int w, h;
int x, y;
int expand_w, expand_h;
txt_widget_t *child;
};
txt_scrollpane_t *TXT_NewScrollPane(int w, int h, TXT_UNCAST_ARG(target));
#endif /* #ifndef TXT_SCROLLPANE_H */

View file

@ -674,6 +674,32 @@ txt_table_t *TXT_NewHorizBox(TXT_UNCAST_ARG(first_widget), ...)
return result;
}
// Get the currently-selected widget in a table, recursively searching
// through sub-tables if necessary.
txt_widget_t *TXT_GetSelectedWidget(TXT_UNCAST_ARG(table))
{
TXT_CAST_ARG(txt_table_t, table);
txt_widget_t *result;
int index;
index = table->selected_y * table->columns + table->selected_x;
result = NULL;
if (index >= 0 && index < table->num_widgets)
{
result = table->widgets[index];
}
if (result != NULL && result->widget_class == &txt_table_class)
{
result = TXT_GetSelectedWidget(result);
}
return result;
}
// Selects a given widget in a table, recursively searching any tables
// within this table. Returns 1 if successful, 0 if unsuccessful.

View file

@ -46,9 +46,12 @@ struct txt_table_s
int selected_y;
};
extern txt_widget_class_t txt_table_class;
txt_table_t *TXT_NewTable(int columns);
txt_table_t *TXT_NewHorizBox(TXT_UNCAST_ARG(first_widget), ...);
void TXT_InitTable(txt_table_t *table, int columns);
txt_widget_t *TXT_GetSelectedWidget(TXT_UNCAST_ARG(table));
void TXT_AddWidget(TXT_UNCAST_ARG(table), TXT_UNCAST_ARG(widget));
void TXT_AddWidgets(TXT_UNCAST_ARG(table), ...);
int TXT_SelectWidget(TXT_UNCAST_ARG(table), TXT_UNCAST_ARG(widget));